J'ai un pipline qui fait juste
command1 | command2
Ainsi, stdout de command1 va à command2, tandis que stderr de command1 va au terminal (ou là où se trouve stdout du Shell).
Comment puis-je diriger stderr de command1 vers un troisième processus (command3
) alors que stdout va toujours commander2?
{ command1 2>&3 | command2; } 3>&1 1>&2 | command3
Vous pouvez utiliser jusqu'à 7 autres descripteurs de fichiers: de 3 à 9.
Si vous voulez plus d'explications, veuillez demander, je peux vous expliquer ;-)
{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'
production:
b2
a1
Produisez deux fichiers journaux:
1. stderr
uniquement
2. stderr
et stdout
{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log
Si command
est echo "stdout"; echo "stderr" >&2
alors on peut le tester comme ça:
$ { { { echo out>&3;echo err>&1;}| tee err-only.log;} 3>&1;} > err-and-stdout.log
$ head err-only.log err-and-stdout.log
==> err-only.log <==
err
==> err-and-stdout.log <==
out
err
La réponse acceptée entraîne l'inversion de stdout
et stderr
. Voici une méthode qui les préserve (puisque Googler à cet effet fait apparaître ce post):
{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
Remarquer:
3>&-
est requis pour empêcher fd d'être hérité par command
. (Comme cela peut conduire à des résultats inattendus en fonction de ce que command
fait à l'intérieur.)Première partie extérieure:
3>&1
- fd pour { ... }
est défini sur ce qu'était fd 1 (c'est-à-dire stdout
)1>&2
- fd 1 pour { ... }
est défini sur ce qu'était fd 2 (c'est-à-dire stderr
)| stdout_command
- fd 1 (était stdout
) est acheminé par stdout_command
La partie interne hérite des descripteurs de fichiers de la partie externe:
2>&1
- fd 2 pour command
est défini sur ce qu'était fd 1 (c'est-à-dire stderr
selon la partie extérieure)1>&3
- fd 1 pour command
est défini sur ce qu'était fd (c'est-à-dire stdout
selon la partie extérieure)3>&-
- fd pour command
est défini sur rien (c'est-à-dire fermé)| stderr_command
- fd 1 (était stderr
) est acheminé par stderr_command
foo() {
echo a
echo b >&2
echo c
echo d >&2
}
{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'
out: a
err: b
err: d
out: c
(Ordre de a -> c
et b -> d
sera toujours indéterminé car il n'y a aucune forme de synchronisation entre stderr_command
et stdout_command
.)
{ command1 | command2; } 2>&1 | command3
Attention:commnd3
lira également command2
stdout (le cas échéant).
Pour éviter cela, vous pouvez jeter commnd2
stdout:
{ command1 | command2 >/dev/null; } 2>&1 | command3
Cependant, pour conserver command2
stdout (par exemple dans le terminal),
veuillez alors vous référer à mon autre réponse plus complexe.
{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'
production:
a2
b2
c2
----12
Utilisation de la substitution de processus:
command1 > >(command2) 2> >(command3)
Voir http://tldp.org/LDP/abs/html/process-sub.html pour plus d'informations.
Le même effet peut être accompli assez facilement avec un fifo. Je ne suis pas au courant d'une syntaxe de tuyauterie directe pour le faire (bien que ce serait chouette d'en voir un). Voici comment vous pourriez le faire avec un fifo.
Tout d'abord, quelque chose qui imprime à la fois sur stdout
et stderr
, outerr.sh
:
#!/bin/bash
echo "This goes to stdout"
echo "This goes to stderr" >&2
Ensuite, nous pouvons faire quelque chose comme ceci:
$ mkfifo err
$ wc -c err &
[1] 2546
$ ./outerr.sh 2>err | wc -c
20
20 err
[1]+ Done wc -c err
De cette façon, vous configurez l'écouteur pour la sortie stderr
en premier et il bloque jusqu'à ce qu'il ait un scripteur, ce qui se produit dans la commande suivante, en utilisant la syntaxe 2>err
. Vous pouvez voir que chaque wc -c
a 20 caractères en entrée.
N'oubliez pas de nettoyer le fifo après avoir terminé si vous ne voulez pas qu'il traîne (c'est-à-dire rm
). Si l'autre commande veut une entrée sur stdin
et non un argument de fichier, vous pouvez utiliser la redirection d'entrée comme wc -c < err
aussi.
Pipe stdout comme d'habitude, mais utilisez la substitution de processus Bash pour la redirection stderr:
some_command 2> >(command of stderr) | command of stdout
Entête: #!/bin/bash