Estou tentando entender o que causa a diferença nessas 2 construções que pensei serem funcionalmente equivalentes:
$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'
stdout: stderr: foo
$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
stderr: foo
EDIT: Se eu entendi user1133275 direito, ele sugere que >(sed 's/^/stdout: /')
não seja executado, a menos que o subshell seja enviado ( echo foo >&2 )
para stdout. No entanto, isso significaria que o seguinte:
$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(echo baz)
baz
stderr: foo
não deve exibir baz
.
EDIT 2: Talvez também de interesse, o sed não produz stdout:
na entrada vazia, mesmo quando canalizado:
$ sed 's/^/stdout: /' < /dev/null
$ printf "" | sed 's/^/stdout: /'
$
Seu primeiro comando,
de forma simplificada (usando um arquivo temporário para armazenar os dados produzidos por
echo
):Ou seja, o primeiro
sed
lê o que é produzido no erro padrão doecho
e grava na saída padrão e o segundosed
lê e modifica isso.Seu segundo comando,
de forma simplificada,
Aqui, o
sed
que obtém a saída de erro padrão produz saída, enquanto o outrosed
que obtém a saída de saída padrão (que não é nada) não produz nenhuma saída (já que não obteve nenhuma entrada e não inseriu ou anexou nenhum dado) .Outra forma de formular:
Primeiro comando:
Segundo comando:
Em suma, o segundo
sed
no segundo comando nunca lê nada. Em particular, ele não lê a saída do primeirosed
como no primeiro comando.Usando símbolos extremamente simplificados, o primeiro comando é algo como
onde
something1
grava na saída padrão, que é lida porsomething2
.O segundo comando, usando a mesma notação,
ou seja
something1
, esomething2
nem mesmo está conectado entre si, esomething2
não pode de forma alguma ler o quesomething1
está produzindo. Além disso, comoutility-writing-to-stderr
não produz nada em seu fluxo de saída padrão,something2
não terá nada para ler de sua entrada padrão.>
está operando( echo foo >&2 )
e|
está operando por>(sed 's/^/stderr: /')
causa da ordem de redirecionamento nos shells. POR EXEMPLOou
vs exemplos explícitos a ordem na pergunta
ou
Os dois comandos não são equivalentes.
O comando (1):
Envia a saída (std) de
two
parathree
(Mais sobre stderr abaixo).O comando (2):
Envia a saída (std) de
one
parathree
(stderr vai paratwo
).O detalhe doloroso é:
Comando 1:
( one ) 2> >(two) | three
three
é iniciado (sed 's/^/stdout: /'
) aguardando entrada em stdin.2
(stderr
) para ostdin
detwo
ocorre.one
ocorre (echo foo >&2
).foo
parastderr
(nada para stdout).foo
é redirecionada de (stderr
deone
) para (stdin
detwo
).sed 's/^/stderr: /'
)two
(stderr: foo
) é enviada para o stdout detwo
.two
passa pelo pipe parathree
.three
que estava aguardando entrada desde o início obtém a saída.stdout:
.stdout: stderr: foo
vai para tty.Comando 2: (um) 2> >(dois) > >(três)
>
) é construído primeiro.three
é iniciado aguardando entradastdin
somente em (>()
).three
obtém apenas o stdout deone
.two
é iniciado aguardando entrada.two
obtém ( somente ) o stderr deone
.one
ao seustderr
.one
é executado enviandofoo
para ostderr
(deone
).foo
vai paratwo
two
altera a string recebida parastderr: foo
e a envia para tty .three
recebe uma entrada vazia do stdout deone
.three
não imprime nada (já que não há linha para processar).Entenda que este comando:
não tem saída (e não há como fazê-lo funcionar).
Mudando a ordem: Comando 3: (um) > >(três) 2> >(dois)
Faz com que a saída do detalhe
two
vá parathree
(não o ) fique como um exercício.tty
Eu votei na resposta muito clara de @Kusalananda, mas me arrisco a (tardiamente) adicionar isso à sua resposta, como uma pequena ilustração da substituição do processo em funcionamento:
Sua segunda proposição funcionaria com uma pequena mudança tipográfica (e uma mudança comparativamente grande em sua lógica defeituosa):
Mas:
Porque adicionar
( )>
conforme ilustrado (ou>( )
, ambos funcionam bem) na verdade permite que você crie um arquivo por meio de substituição de processo, cuja saída pode ser redirecionada como entrada para>(sed 's/^/stdout: /')
.Pensei em adicionar isso para ilustrar como a substituição de processo pode ser incorporada em outra substituição de processo. Para facilitar a leitura, eu não o empurraria para mais níveis de incorporação. Mas se por algum motivo você precisar evitar um pipe e o shell associado , esta é uma boa solução.