5

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

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

#!/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, но пометил другое предложение в качестве ответа.

3 ответа3

2

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

1 Какая бы команда grep -c заканчивалась первой, будет напечатана первая строка вывода.

1

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

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

0

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .