Bash: substituição de processo e stdin

13

A seguinte linha é óbvia:

echo "bla" | foo | bar

Mas os abaixo fazem o mesmo?

echo "bla" | bar <(foo)
echo "bla" | bar < <(foo)

Qual das fooe barleu "bla" de stdin e por quê?

Quero dizer que, é claro, posso apenas codificá-lo e verificá-lo, mas não tenho certeza se é um comportamento definido ou se estou explorando recursos nos quais não devo confiar.

etam1024
fonte

Respostas:

9

Isso depende do shell e não está documentado no AFAICS. No kshe bash, no primeiro caso, foocompartilhará o mesmo padrão que bar. Eles vão lutar pela produção de echo.

Então, por exemplo,

$ seq 10000 | paste - <(tr 1 X)'
1       X
2       X042
3       X043
4       X044
5       X045
[...]

Você vê evidências de que pastelê todos os outros blocos de texto da seqsaída enquanto trlê os outros.

Com zsh, obtém o stdin externo (a menos que seja um terminal e o shell não seja interativo, caso em que é redirecionado /dev/null). ksh(onde se originou) zshe bashsão os únicos reservatórios semelhantes a Bourne com suporte para a substituição do processo AFAIK.

Em echo "bla" | bar < <(foo), observe que baro stdin será o tubo alimentado pela saída de foo. Esse é um comportamento bem definido. Nesse caso, parece que foostdin é o tubo alimentado por echotodos ksh, zshe bash.

Se você deseja ter um comportamento consistente em todas as três conchas e ser à prova de futuro, pois o comportamento pode mudar porque não está documentado, eu escreveria:

echo bla | { bar <(foo); }

Para ter certeza foo, o stdin também é o cachimbo de echo(não consigo ver por que você faria isso). Ou:

echo bla | bar <(foo < /dev/null)

Para se certificar de fooque não ler a partir do tubo de echo. Ou:

{ echo bla | bar 3<&- <(foo <&3); } 3<&0

Ter foostdin o stdin externo como nas versões atuais de zsh.

Stéphane Chazelas
fonte
"Nesse caso, o stdin de foo é o tubo alimentado por eco em todos os ksh, zsh e bash". Você testou isso manualmente agora ou está documentado em algum lugar?
Etam1024
"No primeiro caso" na primeira linha se refere a `| foo | bar` ou `| bar <(foo) `?
Volker Siegel
4

echo "bla" | foo | bar: A saída de echo "bla"é redirecionada para foo, cuja saída é redirecionada bar.

echo "bla" | bar <(foo): Esse canaliza a saída de echo "bla"para bar. Mas baré executado com um argumento. O argumento é o caminho para o descritor de arquivo, para onde a saída de fooserá enviada. Esse argumento se comporta como um arquivo que contém a saída de foo. Então, isso não é o mesmo.

echo "bla" | bar < <(foo): Pode-se supor que a saída de echo "bla"deve ser enviada para bare toda a instrução é equivalente à primeira. Mas isso não está correto. O seguinte acontece: Como o redirecionamento de entrada <é realizado após o pipe ( |), ele o substitui . Portanto, echo "bla"não é canalizado para bar. Em vez disso, a saída de fooé redirecionada como entrada padrão para bar. Para limpar isso, consulte o seguinte comando e saída:

$ echo "bla" | cat < <(echo "foo")
foo

Como você vê, echo "bla"é substituído por echo "foo".

Agora veja este comando:

$ echo "bar" | cat < <(awk '{printf "%s and foo", $0}')
bar and foo

Assim, echo "bar"é canalizado para awk, que lê stdin e adiciona a string and fooà saída. Esta saída é canalizada para cat.

caos
fonte
E minha pergunta é: "O fato de awkler" bar "é um comportamento definido?"
etam1024
Sim, ele é. cmd1 < <(cmd2)se comporta da mesma forma que cmd2 | cmd1.
caos
Ou cmd1 | cmd2 | cmd3é o mesmo quecmd1 | cmd3 < <(cmd2)
caos
3
Se fosse a Wikipedia, eu colocaria a tag "citation needed" em suas declarações :) O ponto principal da minha pergunta é que funciona como você diz, mas não consegui encontrar nenhuma documentação sobre isso.
etam1024