Se você realmente não deseja que o segundo comando prossiga até que o primeiro seja bem-sucedido, provavelmente será necessário usar arquivos temporários. A versão simples disso é:
tmp=${TMPDIR:-/tmp}/mine.$$
if ./a > $tmp.1
then
if ./b <$tmp.1 >$tmp.2
then
if ./c <$tmp.2
then : OK
else echo "./c failed" 1>&2
fi
else echo "./b failed" 1>&2
fi
else echo "./a failed" 1>&2
fi
rm -f $tmp.[12]
O redirecionamento '1> & 2' também pode ser abreviado para '> & 2'; no entanto, uma versão antiga do shell MKS tratou incorretamente o redirecionamento de erro sem o '1' anterior, então usei essa notação inequívoca para confiabilidade por muito tempo.
Isso vaza arquivos se você interromper algo. A programação shell à prova de bomba (mais ou menos) usa:
tmp=${TMPDIR:-/tmp}/mine.$$
trap 'rm -f $tmp.[12]; exit 1' 0 1 2 3 13 15
...if statement as before...
rm -f $tmp.[12]
trap 0 1 2 3 13 15
A primeira linha de trap diz 'execute os comandos' rm -f $tmp.[12]; exit 1
'quando qualquer um dos sinais 1 SIGHUP, 2 SIGINT, 3 SIGQUIT, 13 SIGPIPE ou 15 SIGTERM ocorrer, ou 0 (quando o shell sair por qualquer motivo). Se você estiver escrevendo um script de shell, o trap final só precisa remover o trap em 0, que é o trap de saída do shell (você pode deixar os outros sinais no lugar, pois o processo está prestes a terminar de qualquer maneira).
No pipeline original, é viável para 'c' ler dados de 'b' antes de 'a' terminar - isso geralmente é desejável (dá trabalho a vários núcleos, por exemplo). Se 'b' é uma fase de 'classificação', então isso não se aplica - 'b' tem que ver todas as suas entradas antes de poder gerar qualquer uma das suas saídas.
Se você deseja detectar quais comandos falham, você pode usar:
(./a || echo "./a exited with $?" 1>&2) |
(./b || echo "./b exited with $?" 1>&2) |
(./c || echo "./c exited with $?" 1>&2)
Isso é simples e simétrico - é trivial estender para um pipeline de 4 ou N partes.
A experimentação simples com 'set -e' não ajudou.
&&|
"apenas continue o pipe se o comando anterior for bem-sucedido". Suponho que você também poderia ter, o|||
que significaria "continuar o pipe se o comando anterior falhar" (e possivelmente canalizar a mensagem de erro como o do Bash 4|&
).