Tuyauterie STDERR vs STDOUT

24

Selon " Linux: The Complete Reference 6th Edition " (p. 44), vous ne pouvez diriger que STDERR en utilisant les |&symboles de redirection.

J'ai écrit un script assez simple pour tester ceci:

#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2

Je lance ce script comme ceci:

./script.sh |& sed 's:^:\t:'

Vraisemblablement, seules les lignes imprimées dans STDERR seront en retrait. Cependant, cela ne fonctionne pas vraiment comme ça, comme je le vois:

    Normal Text.
    Error Text. 

Qu'est-ce que je fais mal ici?

Naftuli Kay
la source

Réponses:

26

Je ne sais pas quel texte votre livre utilise, mais le manuel bash est clair (si vous êtes déjà un peu familier avec les redirections):

Si |&est utilisé, l'erreur standard de command1, en plus de sa sortie standard, est connectée à l'entrée standard de command2 via le tuyau; c'est un raccourci pour2>&1 | . Cette redirection implicite de l'erreur standard vers la sortie standard est effectuée après toutes les redirections spécifiées par la commande.

Donc, si vous ne voulez pas mélanger la sortie standard et l'erreur standard, vous devrez rediriger la sortie standard ailleurs. Voir Comment grep flux d'erreur standard (stderr)?

{ ./script.sh 2>&1 >&3 | sed 's:^:\t:'; } 3>&1

Les deux fd 1 et 3 script.shet sedpointeront cependant vers la destination stdout d'origine. Si vous voulez être un bon citoyen, vous pouvez fermer ces fd 3 dont ces commandes n'ont pas besoin:

{ ./script.sh 2>&1 >&3 3>&- | sed 's:^:\t:' 3>&-; } 3>&1

bashet ksh93peut condenser le >&3 3>&-to >&3-(mouvement fd).

Gilles 'SO- arrête d'être méchant'
la source
Le manuel bash n'est pas tout à fait clair pour moi. Je ne comprends pas pourquoi smth comme ./script.sh > /tmp/stdout_goes_here |& grep 'grepping_script_stderr'ne fonctionne pas comme prévu, à savoir: redirect script.shde stdout(qui, selon l'extrait manuel devrait se produire en premier), puis permettre grepde traiter de script stderr. Au lieu de cela, stderret tdout` finissent tous les deux dansstdout_goes_here
sxc731
1
@ sxc731 |&est un raccourci pour 2>&1 |. >/tmp/stdout_goes_here |&Redirige donc stdout vers /tmp/stdout_goes_here, puis 2>&1redirige stderr vers où va stdout, ce qui est /tmp/stdout_goes_here, et |ne reçoit finalement aucune entrée car la sortie de la commande a été redirigée. Gardez à l'esprit que la >&1redirection vers le descripteur de fichier 1 va actuellement , et non vers où le descripteur de fichier 1 finira par aller . Pour diriger stderr uniquement et rediriger stdout vers un fichier, une façon est 2>&1 >/tmp/stdout_goes_here |.
Gilles 'SO- arrête d'être méchant'
6

|&tuyaux stderr vers stdin, comme 2>&1 |, donc le prochain programme obtiendra les deux sur stdin.

$cat test.sh
#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2
$./test.sh | sed 's:^:\t:'
Error Text.
        Normal Text.
$ ./test.sh |& sed 's:^:\t:'
        Normal Text.
        Error Text.
Kevin
la source
Oh, donc c'est fondamentalement équivalent à l'expression plus longue runcommand 2>&1 | tee? c'est à dire runcommand |& tee?
Naftuli Kay
oui, ce sont les mêmes.
Kevin
1

|&dans bash est juste un raccourci (pas terriblement portable) 2>&1 |, donc vous devriez voir chaque ligne en retrait.

jw013
la source