Bash: substituição de processo e stdin

12

A seguinte linha é óbvia:

echo "bla" | foo | bar

Mas os que estão abaixo fazem o mesmo?

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

Qual dos foo e bar leu "bla" de stdin e por quê?

Quero dizer que, é claro, posso 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.

    
por etam1024 05.09.2014 / 10:25

2 respostas

9

Isso é dependente do shell e não é documentado pelo AFAICS. Em ksh e bash , no primeiro caso, foo compartilhará o mesmo status como bar . Eles lutarão pela saída de echo .

Então, por exemplo, em

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

Você vê evidências de que paste lê todos os outros blocos de texto da saída de seq , enquanto tr lê os outros.

Com zsh , ele obtém o stdin externo (a menos que seja um terminal e o shell não seja interativo; nesse caso, ele é redirecionado de /dev/null ). ksh (onde se originou), zsh e bash são as únicas estruturas semelhantes a Bourne com suporte para substituição de processo AFAIK.

Em echo "bla" | bar < <(foo) , observe que o stdin de bar será o pipe alimentado pela saída de foo . Esse é um comportamento bem definido. Nesse caso, parece que o stdin de foo é o pipe alimentado por echo em todos os ksh , zsh e bash .

Se você deseja ter um comportamento consistente em todos os três shells e ser à prova do futuro, já que o comportamento pode mudar, pois não está documentado, eu o escreveria:

echo bla | { bar <(foo); }

Para ter certeza que o stdin de foo também é o canal de echo (não vejo por que você gostaria de fazer isso). Ou:

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

Para garantir que foo não leia não do canal de echo . Ou:

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

Para que foo inclua o stdin externo como nas versões atuais de zsh .

    
por 05.09.2014 / 10:47
4

echo "bla" | foo | bar : A saída de echo "bla" é redirecionada para foo , cuja saída é redirecionada para 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 foo será enviada. Este argumento se comporta como um arquivo que contém a saída de foo . Então, não é o mesmo.

echo "bla" | bar < <(foo) : Pode-se assumir que a saída de echo "bla" deve ser enviada para bar e toda a declaração é equivalente à primeira. Mas isso não está correto. Acontece o seguinte: Como o redirecionamento de entrada < é executado após o canal ( | ), ele sobrescreve-o . Portanto, echo "bla" não é canalizado para a barra. Em vez disso, a saída de foo é redirecionada como entrada padrão para bar . Para limpar isso, veja o seguinte comando e saída:

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

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

Agora veja este comando:

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

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

    
por 05.09.2014 / 10:52