Os comandos em um pipeline são executados ao mesmo tempo, esse é o ponto inteiro dos canais e do mecanismo de comunicação entre processos.
Em:
cmd1 | cmd2
cmd1 e cmd2 são iniciados ao mesmo tempo, cmd2 processa os dados que cmd1 grava conforme aparecem.
Se você quisesse que cmd2 fosse iniciado somente se cmd1 tivesse falhado, você teria que iniciar cmd2 após cmd1 ter terminado e reportado seu status de saída, então você não poderia usar um canal, você teria que usar um arquivo temporário que armazena todos os dados que o cmd1 produziu:
cmd1 > file || cmd2 < file; rm -f file
Ou armazene na memória como no seu exemplo, mas tem vários outros problemas (como $(...) removendo todos os caracteres de nova linha à direita, e a maioria dos shells não consegue lidar com bytes NUL lá, sem mencionar os problemas de dimensionamento de grandes saídas).
No Linux e com shells como zsh ou bash que armazenam aqui-documents e here-strings em arquivos temporários, você poderia fazer:
{ cmd1 > /dev/fd/3 || cmd2 <&3 3<&-; } 3<<< ignored
Para permitir que o shell lide com a criação e limpeza do arquivo temporário.
manualmente, POSIXly:
tmpfile=$(
echo 'mkstemp(template)' |
m4 -D template="${TMPDIR:-/tmp}/XXXXXX"
) && [ -n "$tmpfile" ] && (
rm -f -- "$tmpfile" || exit
cmd1 >&3 3>&- 4<&- ||
cmd2 <&4 4<&- 3>&-) 3> "$tmpfile" 4< "$tmpfile"
Alguns sistemas possuem um comando mktemp não padrão (embora com uma interface que varia entre sistemas) que facilita um pouco a criação do arquivo temporário ( tmpfile=$(mktemp) deve ser suficiente com a maioria da implementação, embora alguns não criem o arquivo então você pode precisar ajustar o umask ). O [ -n "$tmpfile" ] não deve ser necessário com m4 de implementações compatíveis, mas pelo menos o GNU m4 não é compatível, pois não retorna um status de saída diferente de zero quando a chamada mkstemp() falha.
Observe também que não há nada que o impeça de executar qualquer código no console . Seu "script" pode ser inserido da mesma forma no prompt de um shell interativo (exceto pela parte return que assume que o código está em uma função), embora seja possível simplificá-lo para:
output=$(cmd) || grep foo <<< "$output"