Sim, em bash
como em ksh
(de onde vem o recurso), os processos dentro da substituição do processo não são esperados (antes de executar o próximo comando no script).
por <(...)
, geralmente está bem como em:
cmd1 <(cmd2)
o shell estará aguardando por cmd1
e cmd1
estará normalmente aguardando cmd2
em virtude de ler até o final do arquivo no canal substituído e esse fim de arquivo normalmente acontece quando cmd2
morre. Essa é a mesma razão pela qual várias shells (não bash
) não se incomodam em esperar por cmd2
in cmd2 | cmd1
.
Por cmd1 >(cmd2)
, no entanto, esse geralmente não é o caso, já que é mais cmd2
que normalmente espera por cmd1
, então geralmente sairá depois.
Isso foi corrigido em zsh
, que aguarda cmd2
lá (mas não se você o escrever como cmd1 > >(cmd2)
e cmd1
não está embutido, use {cmd1} > >(cmd2)
como documented ).
ksh
não espera por padrão, mas permite que você espere por ele com o wait
incorporado (ele também disponibiliza o pid em $!
, embora isso não ajude se você usar cmd1 >(cmd2) >(cmd3)
)
rc
(com a sintaxe cmd1 >{cmd2}
), igual a ksh
, exceto que você pode obter os pids de todos os processos em segundo plano com $apids
.
es
(também com cmd1 >{cmd2}
) aguarda cmd2
como em zsh
e também espera por cmd2
em <{cmd2}
process redirections.
bash
faz o pid de cmd2
(ou mais exatamente do subshell, já que executa cmd2
em um processo filho desse subshell, embora seja o último comando lá) disponível em $!
, mas não deixa você esperar por isso.
Se você precisar usar bash
, poderá solucionar o problema usando um comando que aguardará os dois comandos com:
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
Isso faz com que ambos cmd1
e cmd2
tenham seu fd 3 aberto para um pipe. cat
aguardará o final do arquivo na outra extremidade, portanto, normalmente só sairá quando cmd1
e cmd2
estiverem inativos. E o shell aguardará esse comando cat
. Você pode ver isso como uma rede para capturar o término de todos os processos em segundo plano (você pode usá-lo para outras coisas iniciados em segundo plano como &
, coprocs ou até mesmo comandos que antecedem eles mesmos, se não fecharem todos os descritores de arquivo como daemons normalmente fazem).
Note que, graças ao processo de subshell desperdiçado mencionado acima, ele funciona mesmo se cmd2
fechar seu fd3 (os comandos geralmente não fazem isso, mas alguns como sudo
ou ssh
do). Versões futuras de bash
podem eventualmente fazer a otimização como em outras shells. Então você precisaria de algo como:
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
Para ter certeza de que ainda há um processo de shell extra com o fd 3 aberto esperando pelo comando sudo
.
Note que cat
não lê nada (já que os processos não escrevem no fd 3). Está lá apenas para sincronização. Ele fará apenas uma chamada de sistema read()
que retornará sem nada no final.
Você pode realmente evitar a execução de cat
usando uma substituição de comando para fazer a sincronização do pipe:
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
Desta vez, é o shell em vez de cat
que está lendo o canal cuja outra extremidade está aberta no fd 3 de cmd1
e cmd2
. Estamos usando uma atribuição de variável para que o status de saída de cmd1
esteja disponível em $?
.
Ou você poderia fazer a substituição do processo manualmente, e então você poderia até mesmo usar o sh
do seu sistema como se fosse a sintaxe padrão do shell:
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
no entanto observe, como observado anteriormente, que nem todas as implementações sh
esperariam por cmd1
após cmd2
ter terminado (embora seja melhor do que o contrário). Nesse momento, $?
contém o status de saída de cmd2
; embora bash
e zsh
disponibilizem o status de saída de cmd1
em ${PIPESTATUS[0]}
e $pipestatus[1]
respectivamente (consulte também a opção pipefail
em alguns shells, portanto $?
pode relatar a falha de outros componentes do duto que o último)
Observe que yash
tem problemas semelhantes com o recurso redirecionamento do processo. cmd1 >(cmd2)
seria escrito cmd1 /dev/fd/3 3>(cmd2)
lá. Mas cmd2
não está aguardando e você não pode usar wait
para esperar por ele e seu pid também não está disponível na variável $!
. Você usaria o mesmo trabalho em torno de bash
.