EDIT: Vejo que descarrilhei e acabei respondendo a uma pergunta diferente daquela feita. A resposta à verdadeira questão está na base da resposta de Paul Tomblin. (Se você quiser aprimorar essa solução para redirecionar stdout e stderr separadamente por algum motivo, pode usar a técnica que descrevo aqui.)
Tenho procurado uma resposta que preserve a distinção entre stdout e stderr. Infelizmente, todas as respostas dadas até agora que preservam essa distinção são propensas a raça: elas correm o risco de os programas receberem informações incompletas, como indiquei nos comentários.
Acho que finalmente encontrei uma resposta que preserva a distinção, não é propensa a raça e também não é terrivelmente complicada.
Primeiro bloco de construção: para trocar stdout e stderr:
my_command 3>&1 1>&2 2>&3-
Segundo bloco de construção: se quiséssemos filtrar (por exemplo, tee) apenas stderr, poderíamos fazer isso trocando stdout & stderr, filtrando e depois trocando de volta:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Agora o resto é fácil: podemos adicionar um filtro stdout, no início:
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
ou no final:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
Para me convencer de que ambos os comandos acima funcionam, usei o seguinte:
alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'
O resultado é:
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
e meu prompt volta imediatamente após o " teed stderr: to stderr
", conforme esperado.
Nota de rodapé sobre zsh :
A solução acima funciona em bash (e talvez em alguns outros shells, não tenho certeza), mas não funciona em zsh. Existem duas razões pelas quais ele falha no zsh:
- a sintaxe
2>&3-
não é compreendida por zsh; que tem que ser reescrito como2>&3 3>&-
- em zsh (ao contrário de outros shells), se você redirecionar um descritor de arquivo que já está aberto, em alguns casos (não entendo completamente como ele decide), ele executa um comportamento semelhante a um tee embutido. Para evitar isso, você deve fechar cada fd antes de redirecioná-lo.
Então, por exemplo, minha segunda solução deve ser reescrita para zsh as {my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(que funciona em bash também, mas é terrivelmente prolixo).
Por outro lado, você pode tirar proveito do misterioso teeing implícito embutido de zsh para obter uma solução muito mais curta para zsh, que não executa tee de forma alguma:
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(Eu não teria adivinhado pelos documentos, descobri que >&1
e 2>&2
são as coisas que acionam a tacada implícita de zsh; descobri isso por tentativa e erro.)