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
16 Options (zsh) — The Z Shell Manual
6 Shell Grammar (zsh) — The Z Shell Manual
15 Parameters (zsh) — The Z Shell Manual
Alterar o shell padrão no Terminal do Mac — Suporte da Apple