O bash pode consumir o mesmo fifo de dois comandos separados?

5

Eu tenho uma enorme fonte de dados que estou filtrando usando grep s.

Veja basicamente o que estou fazendo agora:

#!/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

Repare como estou fazendo a mesma filtragem duas vezes (uma única diferença na segunda vez), calculando a contagem de cada um e dividindo o resultado final. Isso é definitivamente uma coisa hacky para fazer, mas eu gostaria de tentar acelerar um pouco e só executar a filtragem inicial uma vez sem usar um arquivo temporário.

Eu tentei usar um fifo, mas não tenho certeza se é possível ter dois processos em um script lendo a partir dele, assim como ter um terceiro processo "aguardar" até que ambos sejam feitos para calcular o resultado final. Também procurei usar tee , mas novamente não sei como sincronizar os subprocessos resultantes.

EDIT: Resolvi isso sozinho usando o link , mas marquei outra sugestão como resposta.

    
por Andrew 05.03.2013 / 17:27

3 respostas

2

Se você quer apenas evitar a criação de arquivos temporários (ou armazenar a saída do grep em uma variável), você pode alimentá-lo para um loop for assim:

#!/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

Eu criei uma versão modificada da abordagem em sua resposta automática que não exigirá arquivos temporários:

#!/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

A saída dos comandos individuais grep -c e o comando echo são impressos como

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

para evitar condições de corrida 1 . A tubulação para sh executa os comandos impressos.

1 O comando grep -c concluído primeiro imprimirá a primeira linha de saída.

    
por 05.03.2013 / 18:05
1

Acabei resolvendo assim:

#!/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

Então, em vez de confiar em um arquivo fifo ou temporário, usei tee para dividir o fluxo em dois processos separados que geram uma contagem! Dessa forma, não preciso tentar sincronizar os dois processos antes de tentar dividir as contagens.

    
por 05.03.2013 / 19:23
0

Hm, zsh tem um recurso chamado MULTIOS. Com isso, é possível conectar um processo a dois fifos. Se é uma opção aqui, uma pequena demonstração:

#!/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

No entanto, não consegui descobrir uma maneira de contornar a criação do arquivo temporário value1 , que provavelmente deve ser evitado, como apontado por Dennis. Mas talvez você goste desta solução mesmo assim.

    
por 05.03.2013 / 18:21

Tags