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

680
Andrew

У меня есть огромный источник данных, который я фильтрую, используя некоторые 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://superuser.com/a/561248/43649, но пометил другое предложение в качестве ответа.

5
Нет; как только один процесс читает данные из fifo, эти данные исчезают и не будут видны другому процессу. chepner 11 лет назад 0

3 ответа на вопрос

2
Dennis

Если вы просто хотите избежать создания временных файлов (или сохранения вывода 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команда не заканчивалась первой, будет напечатана первая строка вывода.

Супер крутой способ перебрать данные. Andrew 11 лет назад 0
Отлично - это трюк "нужно помнить", чтобы получить значения из подстановок процесса :) mpy 11 лет назад 0
Вот Это Да! Супер крутой способ сделать это. Спасибо за адаптацию моего ответа! Из этого можно многому научиться. Andrew 11 лет назад 0
1
Andrew

Я решил это так:

#!/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для разделения потока на два отдельных процесса, которые просто выводят счет! Таким образом, мне не нужно пытаться синхронизировать два процесса, прежде чем пытаться разделить счетчики.

Вы используете два временных файла ... mpy 11 лет назад 0
Очень интересно! Я включил в ваш ответ модификацию вашего подхода, которая не требует временных файлов. Dennis 11 лет назад 0
В данном случае под словом «не полагаться на временный файл» я понимал временный файл, полный фактических данных. В этом случае это просто сохранение номера. Andrew 11 лет назад 0
0
mpy

Хм, 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, которого, вероятно, следует избегать, как указал Деннис. Но, возможно, вам все же понравится это решение.