Em meus scripts bash, costumo usar pipes e gostaria de saber qual estágio do pipe estava causando o problema em caso de erros. A estrutura básica de tais snippets é:
#!/bin/bash
ProduceCommand 2>/dev/null | ConsumeCommand >/dev/null 2>&1
PipeErrors=("${PIPESTATUS[@]}")
[[ "${PipeErrors[0]}" -eq '0' ]] || { HandleErrorInProduceCommand; }
[[ "${PipeErrors[1]}" -eq '0' ]] || { HandleErrorInConsumeCommand; }
Agora (curiosamente pela primeira vez) estou em uma situação em que seria ótimo se eu pudesse usar tee
ou pee
. Mas o que acontece ao $PIPESTATUS
usar esses comandos? Por exemplo:
#!/bin/bash
ProduceCommand 2>/dev/null | tee >(ConsumeCommand1) >(ConsumeCommand2) >/dev/null 2>&1
PipeErrors=("${PIPESTATUS[@]}")
ou
#!/bin/bash
ProduceCommand 2>/dev/null | pee ConsumeCommand1 ConsumeCommand2 2>/dev/null
PipeErrors=("${PIPESTATUS[@]}")
Acredito que em ambos os casos ${PipeErrors[0]}
reflita o status de erro do ProduceCommand
. Além disso, seria lógico supor que ${PipeErrors[1]}
reflete o status de erro de tee
ou de pee
si mesmo, respectivamente.
Mas isso me leva a pelo menos dois problemas de compreensão:
Qual é o status de erro (valor de retorno) de
tee
oupee
? Não encontrei declarações precisas sobre isso nas páginas de manual. Eles retornam um status de erro codificado se um dos comandos de consumo falhar ou eles retransmitem o status de erro dos comandos de consumo de alguma forma (comossh
, por exemplo)? Se o primeiro for o caso, como podemos descobrir qual dos comandos de consumo é o culpado? Se o último for o caso, qual status de erro é retransmitido? É simplesmente o comando que falha primeiro?AFAIK, bash ou o próprio comando
tee
orpee
, respectivamente, usam pipes (fifos) internamente para obterProduceCommand
a saída de 's para os comandos de consumo. Isso significa que temos um pipe cujo (primeiro e neste caso, único) lado receptor é o próprio pipe. Isso não deve influenciar$PipeErrors
no código de exemplo acima, mas não tenho certeza.
Alguém poderia dar uma luz sobre isso?
É 0 quando conseguiu copiar todos os dados para todos os arquivos de saída e >0 se não. Veja a especificação . A implementação do GNU coreutils
tee
tem opções extras para ignorar erros ao escrever em pipes (como aqueles usados para implementar>(...)
):Não há opções para saber qual das saídas falhou, se houver [1].
Mas sua pergunta parece ser mais sobre se o status de saída dos comandos executados em
>(..)
substituições de processo é refletido de alguma forma emPIPESTATUS
, ou se poderia ser refletido de alguma forma emPIPESTATUS
.A resposta para isso é não .
Em primeiro lugar, observe que
>(...)
são mais como comandos em... &
segundo plano do que comandos de...|...
pipeline. Em um trecho como:não há garantia de que
cmd
tenha terminado no momento em que você executaecho ${PIPESTATUS[@]}
.Mas eles não são exatamente iguais
...&
, pois você nãowait
pode obtê-los e não pode obter seu status de$!
-- exceto em alguns casos limitados que não incluem seu uso comtee
ou outros comandos externos:Como você vê, ambos
tee
e o shell principal terminaram muito antes do comando do>(...)
.[1] Um comando como
pee
o que está executando os "subcomandos" de saída (e está esperando que eles terminem) pode ser mais inteligente e refletir em seu status de saída quais deles falharam (por exemplo, definindo o bit 1 para o primeiro, bit 2 para o segundo, e assim por diante, até 8 subcomandos), mas também não faz isso.Você sempre pode fazer:
Isso lida com o erro em cada um dos subshells em que o produtor e os consumidores são iniciados.
Duplicamos stdout em fd 3, para que possamos restaurar o stdout original para os manipuladores de erros, pois não queremos que sua saída, se houver, passe pelos tubos.
Se você preferir que os manipuladores de erros sejam executados no processo do shell principal (para que ele possa sair, por exemplo), você pode fazer com que esses subshells passem o status de saída para o shell pai por meio de algum canal de substituição de comando:
Isso evita o
$PIPESTATUS
bashism ou você pode evitar o>(...)
kshism e substituí-los por um pipeline normal:Ou combine as duas abordagens e, em seguida, obtenha a
sh
sintaxe padrão e também tenha acesso aotee
status de saída do 's como bônus.Note que
tee
pode morrer de um SIGPIPE se um dos processos sair sem ler toda a entrada, o que significa que o outro processo também pode perder alguma entrada. Portanto, pode ser importante verificar também seu status de saída.Como já observado por @UncleBilly, com a implementação GNU de
tee
, isso pode ser contornado com a-p
opção (ondetee
ignora os sinais SIGPIPE e simplesmente para de tentar gravar mais dados em um pipe quando ele está quebrado).Com outras implementações, você pode substituir
tee ...
por(trap '' PIPE; exec tee ...)
para obter um comportamento semelhante (embora possivelmente algumas mensagens de erro sobre os tubos quebrados, mesmo quetee
não sejam abortadas).