EDIT: Je constate que je me suis égaré et j'ai fini par répondre à une question différente de celle posée. La réponse à la vraie question se trouve en bas de la réponse de Paul Tomblin. (Si vous souhaitez améliorer cette solution pour rediriger stdout et stderr séparément pour une raison quelconque, vous pourriez utiliser la technique que je décris ici.)
J'ai cherché une réponse qui préserve la distinction entre stdout et stderr. Malheureusement, toutes les réponses données jusqu'à présent qui préservent cette distinction sont sujettes aux courses : elles risquent que les programmes voient une entrée incomplète, comme je l'ai souligné dans les commentaires.
Je pense avoir enfin trouvé une réponse qui préserve la distinction, qui ne provoque pas de courses, et qui n'est pas trop compliquée non plus.
Premier bloc de construction : pour échanger stdout et stderr :
my_command 3>&1 1>&2 2>&3-
Deuxième bloc de construction : si nous voulions filtrer (par exemple avec tee) seulement stderr, nous pourrions y parvenir en échangeant stdout&stderr, en filtrant, puis en échangeant à nouveau :
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Maintenant, le reste est facile : nous pouvons ajouter un filtre stdout, soit au début :
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
soit à la fin :
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
Pour me convaincre que les deux commandes ci-dessus fonctionnent, j'ai utilisé ce qui suit :
alias my_command='{ echo "vers stdout"; echo "vers 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;}'
La sortie est :
...(pause d'1 seconde)...
teed stdout: vers stdout
...(autre pause de 1 seconde)...
teed stderr: vers stderr
et mon invite de commande revient immédiatement après le "teed stderr: vers stderr
", comme prévu.
Note à propos de zsh :
La solution ci-dessus fonctionne dans bash (et peut-être dans d'autres shells, je ne suis pas sûr), mais elle ne fonctionne pas dans zsh. Il y a deux raisons pour lesquelles elle échoue dans zsh :
- la syntaxe
2>&3-
n'est pas comprise par zsh ; elle doit être réécrite en 2>&3 3>&-
- dans zsh (contrairement à d'autres shells), si vous redirigez un descripteur de fichier qui est déjà ouvert, dans certains cas (je ne comprends pas complètement comment il décide), il réalise un comportement similaire à tee. Pour éviter cela, vous devez fermer chaque fd avant de le rediriger.
Ainsi, par exemple, ma deuxième solution doit être réécrite pour zsh comme {my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(ce qui fonctionne également dans bash, mais est assez verbeux).
En revanche, vous pouvez profiter de l'écriture mystérieuse intégrée de zsh pour obtenir une solution beaucoup plus courte pour zsh, qui n'exécute pas tee du tout :
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(Je n'aurais pas deviné, d'après les documents que j'ai trouvés, que >1
et 2>&2
sont ce qui déclenche le teeing implicite de zsh ; j'ai découvert cela par tâtonnement.)
0 votes
Pour les autres lecteurs : question similaire : stackoverflow.com/questions/692000/…