Para fazer com que um pipeline do zsh reporte uma falha a partir de qualquer estágio, ative a opção PIPE_FAIL antes de executá-lo. No Terminal do Mac, digite setopt PIPE_FAIL; em um script, coloque essa linha antes do pipeline. O pipeline retorna então o status de saída do seu comando com falha mais à direita, ou zero se todos os comandos forem bem-sucedidos. Capture $? imediatamente após o pipeline caso comandos posteriores não devam substituir esse resultado. O manual de opções do zsh documenta esse comportamento.

Por que um pipeline com falha pode parecer bem-sucedido

Por padrão, o zsh atribui a um pipeline o status de saída de seu comando final. Assim, se um comando anterior falhar, mas o comando final for bem-sucedido, o pipeline como um todo pode parecer ter sido bem-sucedido. Por exemplo, o cat final abaixo pode ser bem-sucedido mesmo quando o grep não encontra nenhuma linha correspondente:

printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?

O manual do zsh define um pipeline como comandos unidos por | e afirma que seu valor é normalmente o valor do último comando. Um status zero significa sucesso; um status diferente de zero significa que o comando ou pipeline reportou falha. A documentação de pipelines do zsh descreve a regra padrão.

Ative o PIPE_FAIL para o pipeline

Em uma sessão do Terminal, defina a opção antes de executar o comando:

setopt PIPE_FAIL
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?

O status final agora é diferente de zero porque o grep falhou, mesmo que o cat tenha sido bem-sucedido. O PIPE_FAIL usa o status diferente de zero mais à direita no pipeline. Isso mantém a falha significativa enquanto ainda retorna o código de status real selecionado pelo zsh. Para desativar esse comportamento novamente na mesma sessão, execute unsetopt PIPE_FAIL.

Para um script, posicione a opção perto do início, após a linha do interpretador e antes de qualquer pipeline afetado:

#!/bin/zsh
setopt PIPE_FAIL

printf '%s\n' 'ready' | grep 'missing' | cat
pipeline_status=$?

if (( pipeline_status != 0 )); then
  print -u2 -- "Pipeline failed with status $pipeline_status"
  exit "$pipeline_status"
fi

A atribuição explícita de status é importante: $? significa o status do último comando, portanto, um print posterior, um comando relacionado a variáveis ou uma condicional podem alterar o que ele reporta. Salvar o valor imediatamente permite que o script o reporte ou saia com ele após outros trabalhos. Este exemplo retorna um status de falha para o programa que inicializou o script; ele não impede que comandos individuais do pipeline continuem sendo executados assim que um estágio anterior falhar.

Use pipestatus quando precisar do resultado de cada estágio

O PIPE_FAIL resume o pipeline. Se você também precisar identificar qual comando retornou qual código, o zsh fornece o array especial pipestatus, contendo o status de cada comando no último pipeline. Leia-o imediatamente, pois outro pipeline substitui esses valores. A referência de parâmetros do zsh documenta o array.

printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- "Stage statuses: ${pipestatus[*]}"

Para este exemplo de três comandos, a saída mostra um zero para o printf, um valor diferente de zero para o grep e um zero para o cat. O código exato diferente de zero depende do comando. Se precisar tanto do resultado geral quanto dos resultados por estágio, use PIPE_FAIL e capture o $? geral imediatamente; inspecione pipestatus imediatamente após a execução de um pipeline separado ao diagnosticar estágios, ou projete o script para registrar o array primeiro e calcular o agregado desejado a partir desses dados salvos. Evite inserir outro comando ou pipeline entre o pipeline e o status que você pretende ler.

Mantenha a configuração no shell correto

Para aplicar a opção a comandos posteriores em uma sessão interativa do zsh, execute setopt PIPE_FAIL nessa sessão. Para ativá-la para cada nova sessão do zsh, adicione a linha ao ~/.zshrc, depois abra uma nova janela do Terminal ou carregue o arquivo com source ~/.zshrc. O guia do Terminal da Apple identifica o zsh como o shell padrão, observando também que configurações ou administradores podem selecionar um shell diferente. Confirme o shell ativo antes de confiar em sintaxes específicas do zsh. O guia do Terminal da Apple explica como o Terminal escolhe o shell.

Um script que começa com #!/bin/zsh solicita o zsh diretamente, independentemente do shell interativo configurado para o Terminal. Se, em vez disso, o script for executado por outro shell, setopt e pipestatus não são substitutos portáteis para os controles de pipeline próprios daquele shell. Mantenha o interpretador e a sintaxe alinhados e, em seguida, preserve o resultado do pipeline antes de realizar tarefas posteriores.

Fontes