EDIT: Vedo che sono stato deragliato e ho finito per rispondere a una domanda diversa da quella posta. La risposta alla vera domanda è in fondo alla risposta di Paul Tomblin. (Se vuoi migliorare quella soluzione per reindirizzare stdout e stderr separatamente per qualche motivo, potresti usare la tecnica che descrivo qui.)
Volevo una risposta che preservasse la distinzione tra stdout e stderr. Sfortunatamente tutte le risposte date finora che conservano tale distinzione sono inclini alla corsa: rischiano che i programmi vedano input incompleti, come ho sottolineato nei commenti.
Penso di aver finalmente trovato una risposta che preserva la distinzione, non è incline alla razza e non è nemmeno troppo complicata.
Primo blocco di costruzione: per scambiare stdout e stderr:
my_command 3>&1 1>&2 2>&3-
Secondo elemento costitutivo: se volessimo filtrare (ad esempio tee) solo stderr, potremmo farlo scambiando stdout e stderr, filtrando e poi scambiando di nuovo:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Ora il resto è facile: possiamo aggiungere un filtro stdout, o all'inizio:
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
o alla fine:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
Per convincermi che entrambi i comandi precedenti funzionino, ho utilizzato quanto segue:
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;}'
L'output è:
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
e il mio messaggio ritorna immediatamente dopo il " teed stderr: to stderr", come previsto.
Nota a piè di pagina su zsh :
La soluzione di cui sopra funziona in bash (e forse alcune altre shell, non ne sono sicuro), ma non funziona in zsh. Ci sono due ragioni per cui fallisce in zsh:
- la sintassi
2>&3-non è compresa da zsh; che deve essere riscritto come2>&3 3>&-
- in zsh (a differenza di altre shell), se reindirizzi un descrittore di file che è già aperto, in alcuni casi (non capisco completamente come decide) fa invece un comportamento tipo tee incorporato. Per evitare ciò, è necessario chiudere ogni fd prima di reindirizzarlo.
Quindi, ad esempio, la mia seconda soluzione deve essere riscritta per 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(che funziona anche in bash, ma è terribilmente prolissa).
D'altra parte, puoi sfruttare il misterioso tee implicito incorporato di zsh per ottenere una soluzione molto più breve per zsh, che non esegue affatto tee:
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(Non avrei immaginato dai documenti di aver scoperto che >&1e 2>&2sono la cosa che fa scattare il tee implicito di zsh; l'ho scoperto per tentativi ed errori.)