web-dev-qa-db-fra.com

diriger stdout et stderr vers deux processus différents dans le script Shell?

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?

55
user964970

Utilisez un autre descripteur de fichier

{ 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 ;-)

Tester

{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'

production:

b2
a1

Exemple

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
53
olibre

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.)

Pièces expliquées:

  1. Première partie extérieure:

    1. 3>&1 - fd pour { ... } est défini sur ce qu'était fd 1 (c'est-à-dire stdout)
    2. 1>&2 - fd 1 pour { ... } est défini sur ce qu'était fd 2 (c'est-à-dire stderr)
    3. | stdout_command - fd 1 (était stdout) est acheminé par stdout_command
  2. La partie interne hérite des descripteurs de fichiers de la partie externe:

    1. 2>&1 - fd 2 pour command est défini sur ce qu'était fd 1 (c'est-à-dire stderr selon la partie extérieure)
    2. 1>&3 - fd 1 pour command est défini sur ce qu'était fd (c'est-à-dire stdout selon la partie extérieure)
    3. 3>&- - fd pour command est défini sur rien (c'est-à-dire fermé)
    4. | stderr_command - fd 1 (était stderr) est acheminé par stderr_command

Exemple:

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: /'

Production:

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.)

25
antak

Redirigez simplement stderr vers stdout

{ 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.

Tester

{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'

production:

a2
b2
c2
----12
13
olibre

Utilisation de la substitution de processus:

command1 > >(command2) 2> >(command3)

Voir http://tldp.org/LDP/abs/html/process-sub.html pour plus d'informations.

5
FuePi

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.

1
FatalError

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

0
iBug