
Estou tentando entender como o status de saída é comunicado quando um pipe é usado. Suponha que eu esteja usando which
para localizar um programa inexistente:
which lss
echo $?
1
Como which
não consegui localizar, lss
obtive um status de saída 1. Tudo bem. No entanto, quando tento o seguinte:
which lss | echo $?
0
Isso indica que o último comando executado foi encerrado normalmente. A única maneira de entender isso é que talvez o PIPE também produza um status de saída. Essa é a maneira correta de entender isso?
Responder1
O status de saída de um pipe é o status de saída do comando da direita. O status de saída do comando esquerdo é ignorado.
(Observe que isso which lss | echo $?
não mostra isso. Você executaria which lss | true; echo $?
para mostrar isso. Em which lss | echo $?
, echo $?
relata o status do último comando antes deste pipeline.)
A razão pela qual os shells se comportam dessa maneira é que existe um cenário bastante comum em que um erro no lado esquerdo deve ser ignorado. Se o lado direito sair (ou, mais geralmente, fechar sua entrada padrão) enquanto o lado esquerdo ainda estiver escrevendo, o lado esquerdo receberá um sinal SIGPIPE. Neste caso, geralmente não há nada de errado: o lado direito não se importa com os dados; se a função do lado esquerdo for apenas produzir esses dados, então não há problema em parar.
No entanto, se o lado esquerdo morrer por algum motivo diferente do SIGPIPE, ou se a tarefa do lado esquerdo não for apenas produzir dados na saída padrão, então um erro no lado esquerdo é um erro genuíno que deveria ser relatado.
No sh simples, a única solução é usar um pipe nomeado.
set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1
Em ksh, bash e zsh, você pode instruir o shell para fazer um pipeline sair com status diferente de zero se qualquer componente do pipeline sair com status diferente de zero. Você precisa definir a pipefail
opção:
- ksh:
set -o pipefail
- festa:
shopt -s pipefail
- zsh:
setopt pipefail
(ousetopt pipe_fail
)
Em mksh, bash e zsh, você pode obter o status de cada componente do pipeline usando a variável PIPESTATUS
(bash, mksh) ou pipestatus
(zsh), que é um array contendo o status de todos os comandos no último pipeline (uma generalização de $?
).
Responder2
O status de saída do pipeline é o status de saída do último comando no pipeline (a menos que a opção shell pipefail
esteja definida nos shells que o suportam; nesse caso, o status de saída será o do último comando no pipeline que sai com um status diferente de zero).
Não é possível gerar o status de saída de um pipelinede dentroum pipeline, já que não há como saber qual seria esse valor até que o pipeline tenha realmente terminado a execução.
O gasoduto
which lss | echo $?
não faz sentido, pois echo
não lê sua entrada padrão (pipelines são usados para passar dados entre a saída de um comando e a entrada do próximo). O echo
também não imprimiria o status de saída do pipeline, mas o status de saída do comando seria executado imediatamenteanteso gasoduto.
$ false
$ which hello | echo $?
1
which: hello: Command not found.
$ true
$ which hello | echo $?
0
which: hello: Command not found.
Este é um exemplo melhor:
$ echo hello | read a
$ echo $?
0
$ echo nonexistent | { read filename && rm $filename; }
rm: nonexistent: No such file or directory
$ echo $?
1
Isso significa que um pipeline também pode ser usado com if
:
if gzip -dc file.gz | grep -q 'something'; then
echo 'something was found'
fi
Responder3
Sim, o PIPE produz um status de saída; se quiser o código de saída do comando antes do PIPE, você pode usar$PIPESTATUS
De acordo comestas perguntas e respostas sobre Stack Overflow, para imprimir o status de saída de um comando ao usar um canal, você pode fazer:
your_command | echo $PIPESTATUS
Ou defina pipefail com o comando set -o pipefail
(para desativá-lo, faça set +o pipefail
) e use $?
em vez de $PIPESTATUS
.
your_command | echo $?
Esses comandos só funcionam depois de serem executados pelo menos uma vez; isso significa que PIPESTATUS
só funciona quando você o executa após o comando com o pipe, assim:
command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
Você receberá 10 por ${PIPESTATUS[0]}
e 1 por ${PIPESTATUS[1]}
. Verestas perguntas e respostas sobre U&LPara maiores informações.
Responder4
O shell avalia a linha de comando antes que o pipeline seja executado. O mesmo $?
ocorre com o valor do último comando executado antes do pipeline. Se echo
ele foi iniciado antes ou depois which
é irrelevante.
Agora se sua dúvida era especificamente sobre a propagação do status de saída, você pode estudar o comportamento padrão assim:
% false | true
% echo $?
0
% true | false
% echo $?
1
Como você pode ver, o status de saída de um pipeline é o status de saída do seu último comando.