web-dev-qa-db-fra.com

Comment canaliser stderr, et pas stdout?

J'ai un programme qui écrit des informations dans stdout et stderr, et j'ai besoin de grep à travers ce qui arrive à stderr, en ignorant stdout.

Je peux bien sûr le faire en 2 étapes:

command > /dev/null 2> temp.file
grep 'something' temp.file

mais je préférerais pouvoir le faire sans fichiers temporaires. Existe-t-il des astuces de tuyauterie intelligentes?

910
user80168

D'abord, redirigez stderr vers stdout - le tuyau; puis redirigez stdout vers /dev/null (sans changer où stderr va):

command 2>&1 >/dev/null | grep 'something'

Pour plus de détails sur la redirection des E/S dans toute sa diversité, voir le chapitre Redirections dans le manuel de référence de Bash.

Notez que la séquence des redirections d'E/S est interprétée de gauche à droite, mais que les canaux sont configurés avant que les redirections d'E/S ne soient interprétées. Les descripteurs de fichier tels que 1 et 2 sont des références aux descriptions de fichier ouvertes. L'opération 2>&1 fait que le descripteur de fichier 2, appelé aussi stderr, renvoie à la même description de fichier ouverte que le descripteur de fichier 1, c'est-à-dire que stdout fait actuellement référence à (voir dup2() et open() ). . L'opération >/dev/null modifie ensuite le descripteur de fichier 1 afin qu'il fasse référence à une description de fichier ouverte pour /dev/null, mais cela ne change pas le fait que le descripteur de fichier 2 fait référence à la description de fichier ouverte, descripteur de fichier 1. montrait à l'origine - à savoir, le tuyau.

1083
Jonathan Leffler

Ou pour échanger la sortie de stderr et stdout sur l'utilisation: -

command 3>&1 1>&2 2>&3

Cela crée un nouveau descripteur de fichier (3) et l’assigne au même endroit que 1 (stdout), puis assigne fd 1 (stdout) au même endroit que fd 2 (stderr) et attribue enfin fd 2 (stderr) au même placer comme fd 3 (stdout). Stderr est maintenant disponible en tant que stdout et l'ancien stdout est conservé dans stderr. C'est peut-être excessif, mais j'espère donner plus de détails sur les descripteurs de fichier bash (9 processus sont disponibles pour chaque processus).

341
Kramish

Dans Bash, vous pouvez également rediriger vers un sous-shell en utilisant substitution de processus :

command > >(stdlog pipe)  2> >(stderr pipe)

Pour le cas présent:

command 2> >(grep 'something') >/dev/null
199
Rich Johnson

En combinant les meilleures de ces réponses, si vous le faites:

command 2> >(grep -v something 1>&2)

... alors tout stdout est conservé comme stdout et tout stderr est conservé comme stderr, mais vous ne verrez aucune ligne dans stderr contenant la chaîne "quelque chose".

Cela présente l’avantage unique de ne pas inverser ni de supprimer stdout et stderr, ni de les fusionner, ni d’utiliser de fichiers temporaires.

173
Pinko

Il est beaucoup plus facile de visualiser les choses si vous réfléchissez à ce qui se passe réellement avec les "redirections" et les "pipes". Les redirections et les pipes dans bash font une chose: modifier l'endroit où les descripteurs de fichier de processus 0, 1 et 2 pointent (voir/proc/[pid]/fd/*).

Quand un pipe ou "|" L’opérateur est présent sur la ligne de commande. La première chose à faire est que bash crée un fifo et pointe le FD 1 de la commande de gauche sur ce fifo et pointe le FD 0 de la commande de droite sur le même fifo.

Ensuite, les opérateurs de redirection de chaque côté sont évalués de gauche à droite , et les paramètres actuels sont utilisés chaque fois que le descripteur est dupliqué. Ceci est important car, depuis que le tuyau a été installé en premier, les FD1 (côté gauche) et FD0 (côté droit) ont déjà été modifiés, et toute duplication de ceux-ci reflétera ce fait.

Par conséquent, lorsque vous tapez quelque chose comme ce qui suit:

command 2>&1 >/dev/null | grep 'something'

Voici ce qui se passe, dans l'ordre:

  1. un tuyau (fifo) est créé. "commande FD1" est pointé sur ce tuyau. "grep FD0" est également pointé sur ce tuyau
  2. "commande FD2" pointe vers l'endroit où "commande FD1" pointe actuellement (le canal)
  3. "commande FD1" pointe vers/dev/null

Ainsi, toutes les sorties que "commande" écrit sur son FD 2 (stderr) se dirigent vers le canal et sont lues par "grep" de l'autre côté. Toutes les sorties que "commande" écrit sur son FD 1 (stdout) se rendent vers/dev/null.

Si à la place, vous exécutez ce qui suit:

command >/dev/null 2>&1 | grep 'something'

Voici ce qui se passe:

  1. un tuyau est créé et on lui fait "commande FD 1" et "grep FD 0"
  2. "commande FD 1" est pointé sur/dev/null
  3. "commande FD 2" pointe vers l'endroit où FD 1 pointe actuellement (/ dev/null)

Donc, tous les stdout et stderr de "commande" vont dans/dev/null. Rien ne va au tuyau, et donc "grep" se fermera sans rien afficher à l'écran.

Notez également que les redirections (descripteurs de fichier) peuvent être en lecture seule (<), en écriture seule (>) ou en lecture-écriture (<>).

Une note finale. Que ce soit un programme écrit quelque chose sur FD1 ou FD2, cela dépend entièrement du programmeur. Selon les bonnes pratiques de programmation, les messages d'erreur doivent aller au FD 2 et la sortie normale au FD 1, mais vous trouverez souvent une programmation bâclée qui mélange les deux ou ignore la convention.

95
Michael Martinez

Utilisez-vous bash? Si c'est le cas:

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines

33
Ken Sharp

Pour ceux qui veulent rediriger stdout et stderr de façon permanente vers des fichiers, grep sur stderr, mais conservez stdout pour écrire des messages dans un tty:

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3
9
JBD

Cela redirigera commande1 stderr vers commande2 stdin, tout en laissant commande1 stdout en l'état.

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

Tiré de LDP

5
theDolphin

Je viens juste de proposer une solution pour envoyer stdout à une commande et stderr à une autre, en utilisant des canaux nommés.

Voici.

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

C'est probablement une bonne idée de supprimer les canaux nommés par la suite.

1
Tripp Kinetics

Vous pouvez utiliser le shell rc .

Commencez par installer le paquet (moins de 1 Mo).

Voici un exemple de la façon dont vous écarteriez stdout et dirigeriez stderr vers grep dans rc:

find /proc/ >[1] /dev/null |[2] grep task

Vous pouvez le faire sans quitter Bash:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

Comme vous l'avez peut-être remarqué, vous pouvez spécifier le descripteur de fichier que vous souhaitez transférer en utilisant des crochets après le canal.

Les descripteurs de fichier standard sont numérotés comme tels:

  • 0: entrée standard
  • 1: Ouptut standard
  • 2: erreur type
0
Rolf