Existe alguma maneira de ler linhas da saída do comando?

8

Eu tenho um comando de pré-processo para gerar um arquivo

./preprocess.sh > preprocessed_file 

e o preprocessed_fileserá usado assim

while read line
do

    ./research.sh $line &

done < preprocessed_file 

rm -f preprocessed_file

Existe alguma maneira de direcionar a saída para a while read linepeça em vez de enviar para o preprocessed_file? Eu acho que deveria haver uma maneira melhor do que usar esta temperatura preprocessed_file.

Marcus Thornton
fonte

Respostas:

8

Você pode usar a substituição do processo bash :

while IFS= read -r line; do
  ./research.sh "$line" &
done < <(./preprocess.sh)

Algumas vantagens da substituição de processo:

  • Não há necessidade de salvar arquivos temporários.
  • Melhor performance. Leitura de outro processo geralmente mais rápido do que gravar no disco e depois ler novamente.
  • Economize tempo no cálculo, uma vez que é realizado simultaneamente com expansão de parâmetro e variável, substituição de comando e expansão aritmética
cuonglm
fonte
o que significam as setas duplas à esquerda (<<)?
Marcus Thornton
@ MarcusThornton: <é um redirecionamento, enquanto <(...)é sintaxe de substituição de processo. Você deve ler: gnu.org/software/bash/manual/html_node/… para obter mais detalhes.
precisa saber é o seguinte
Entendi. <(...)faz parte da sintaxe.
Marcus Thornton
2
Não é necessariamente mais rápido. Porque ao ler de um canal, readé necessário ler um byte de cada vez, enquanto ele pode otimizar as coisas com a leitura de partes maiores e procurar retroceder ao ler de um arquivo comum. O melhor é evitar os while readloops completamente, em primeiro lugar, quando possível. Observe também que você precisa IFS= read -r lineler a linha $line. E deixar sem $lineaspas (invocando o operador split + glob) aqui provavelmente não faz sentido.
Stéphane Chazelas
1
@mikeserv, os comandos geralmente fazem buffer de linha (em oposição ao buffer completo) sua saída quando se dirige a um terminal. Aqui estou dizendo que o readshell interno lê um caractere de cada vez ao ler de um tubo (independentemente do que está na outra extremidade do tubo que readnão tem como saber), que é um dos motivos pelos quais os while readloops são tremendamente lentos.
Stéphane Chazelas
15

Sim! Você pode usar um tubo de processo |.

./preprocess.sh |
    while IFS= read -r line
    do
        ./research.sh "$line" &
    done

Um tubo de processo passa a saída padrão ( stdout) de um processo para a entrada padrão ( stdin) do próximo.

Opcionalmente, você pode colocar um caractere de nova linha após a |e estender o comando para a próxima linha.

Nota: a|bé equivalente a b < <(a), mas sem os arquivos mágicos, e em uma ordem mais legível, especialmente quando o pipeline fica mais longo.

a|b|c é equivalente a c < <(b < <(a))

e

a|b|c|d|e é e < < (d < <(c < <(b < <(a))))

ctrl-alt-delor
fonte
3
Nota: Esta solução com o pipe tem a vantagem de ser mais portátil do que a substituição do processo (não suportada por alguns shells POSIX como o dash). Ainda no que diz respeito à portabilidade, o lado direito de um pipe pode ser executado em um subshell (isso depende do shell), para que qualquer efeito colateral (como a definição de variáveis) possa não afetar o ambiente do script de shell.
precisa saber é
Geralmente é mais seguro colocar referências de variáveis ​​como $lineaspas duplas (por exemplo, no seu script ./research.sh "$line" &).
G-Man diz 'Reinstate Monica
1
@ G-Man Possivelmente não neste contexto. Se research.shfuncionar com a matriz de argumentos da linha de comando e $linefor, por exemplo, "um dois", com a intenção de que o primeiro argumento seja "um" e o segundo argumento "dois", a citação $linetornará isso impossível - em vez disso, o primeiro argumento será "um dois" e não haverá um segundo ...
Goldilocks
2
" a|bé equivalente ab < <(a) " - fechar, mas não exatamente. Na versão do canal, os dois lados do canal são executados em subcascas, enquanto na versão de substituição de processo, apenas o processo substituído é executado em uma subcasca, mas aé executado no escopo do nível do shell atualmente em execução. Isso tem implicações importantes para o escopo das variáveis ​​definidas em #a
Digital Trauma