Status de saída do Bash usado com o PIPE

8

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

which lss
echo $?
1

Como which falhou ao localizar lss , obtive um status de saída igual a 1. Isso está correto. No entanto, quando tento o seguinte:

which lss | echo $?
0

Isso indica que o último comando executado saiu normalmente. A única maneira de entender isso é que talvez o PIPE também produza um status de saída. É este o caminho certo para entender isso?

    
por sshekhar1980 20.05.2017 / 15:39

4 respostas

12

O status de saída do pipeline é o status de saída do último comando no pipeline (a menos que a opção pipefail do shell esteja definida nos shells que a suportam, caso em que o status de saída será o do último comando no pipeline que sai com um status diferente de zero).

Não é possível exibir o status de saída de um pipeline de dentro de um pipeline, pois não há como saber qual seria esse valor até que o pipeline tenha realmente concluído a execução.

O pipeline

which lss | echo $?

é sem sentido porque echo não lê sua entrada padrão (os pipelines são usados para passar dados entre a saída de um comando para a entrada do próximo). O echo também não imprimia o status de saída do pipeline, mas o status de saída do comando é executado imediatamente antes do pipeline.

$ 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
    
por 20.05.2017 / 15:56
10

O status de saída de um canal é o status de saída do comando à direita. O status de saída do comando da mão esquerda é ignorado.

(Observe que which lss | echo $? não mostra isso. Você executaria which lss | true; echo $? para mostrar isso. Em which lss | echo $? , echo $? informa o status do último comando antes desse pipeline).

A razão pela qual os shells se comportam dessa maneira é que há um cenário bastante comum em que um erro no lado esquerdo deve ser ignorado. Se o lado direito sai (ou mais geralmente fecha sua entrada padrão) enquanto o lado esquerdo ainda está escrevendo, então o lado esquerdo recebe um sinal SIGPIPE. Nesse caso, geralmente não há nada errado: o lado direito não se importa com os dados; Se o papel do lado esquerdo é apenas para produzir esses dados, então está tudo bem para ele parar.

No entanto, se o lado esquerdo for interrompido por alguma razão diferente do SIGPIPE, ou se o trabalho do lado esquerdo não foi unicamente para produzir dados na saída padrão, então um erro no lado esquerdo é um erro. erro genuíno que deve ser relatado.

Em 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 a fazer uma saída de pipeline com um status diferente de zero se qualquer componente do pipeline sair com um status diferente de zero. Você precisa definir a opção pipefail :

  • ksh: set -o pipefail
  • bash: 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 é uma matriz contendo o status de todos os comandos no último pipeline (uma generalização de $? ).

    
por 20.05.2017 / 15:51
4

Sim, o PIPE produz um status de saída, se você quiser o código de saída do comando antes do PIPE, você pode usar $PIPESTATUS

De acordo com isso para imprimir o status de saída de um comando quando você usa um pipe que você pode fazer:

your_command | echo $PIPESTATUS

Ou defina pipefail com o comando set -o pipefail (para remover a definição set +o pipefail ) e use $? em vez de $PIPESTATUS .

your_command | echo $?

Estes comandos só funcionam depois de você executá-los pelo menos uma vez, o que parece PIPESTATUS só funciona quando é executado 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 } Verifique this para obter mais informações. this p>     

por 20.05.2017 / 16:19
0

O shell avalia a linha de comando antes que o pipeline seja executado. Então, $? é o valor do último comando executado antes do pipeline. Se echo é iniciado antes ou depois de which ser irrelevante.

Agora, se sua pergunta foi 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 de seu último comando.

    
por 21.05.2017 / 20:50