Status de saída do Bash usado com PIPE

Status de saída do Bash usado com PIPE

Estou tentando entender como o status de saída é comunicado quando um pipe é usado. Suponha que eu esteja usando whichpara localizar um programa inexistente:

which lss
echo $?
1

Como whichnão consegui localizar, lssobtive 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 pipefailopção:

  • ksh:set -o pipefail
  • festa:shopt -s pipefail
  • zsh: setopt pipefail(ou setopt 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 pipefailesteja 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 echonã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 echotambé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 PIPESTATUSsó 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 echoele 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.

informação relacionada