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?
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.
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).
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
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.
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:
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:
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.
Utilisez-vous bash? Si c'est le cas:
command >/dev/null |& grep "something"
http://www.gnu.org/software/bash/manual/bashref.html#Pipelines
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
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
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.
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: