Comment pipe stderr, et pas stdout?
j'ai un programme qui écrit des informations à stdout
et stderr
, et j'ai besoin de grep
à travers ce qui vient à stderr , tout 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 temp. Existe-il des smart tuyauterie astuces?
10 réponses
rediriger d'abord stderr vers stdout - le tuyau; puis rediriger stdout vers /dev/null
(sans changer où stderr va):
command 2>&1 >/dev/null | grep 'something'
pour les détails de la redirection I/O dans toute sa variété, voir le chapitre Redirections dans le manuel de référence de Bash.
noter que la séquence des redirections d'e/s est interprétée de gauche à droite, mais les pipes sont configurées avant que les redirections d'e/s soient interprétées. Fichier les descripteurs tels que 1 et 2 sont des références à des descriptions de fichiers ouverts. L'opération 2>&1
rend le descripteur de fichier 2 aka stderr se référer à la même description de fichier ouvert que le descripteur de fichier 1 aka stdout se réfère actuellement à (voir dup2()
et open()
). L'opération >/dev/null
modifie alors le descripteur de fichier 1 de sorte qu'il se réfère à une description de fichier ouvert pour /dev/null
, mais cela ne change pas le fait que le fichier le descripteur 2 fait référence à la description du fichier ouvert sur lequel le descripteur de fichier 1 pointait à l'origine, à savoir la pipe.
ou pour échanger la sortie de stderr et stdout sur l'utilisation: -
command 3>&1 1>&2 2>&3
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 finalement assigne fd 2 (stderr) au même endroit que fd 3 (stdout). Stderr est maintenant disponible en stdout et vieux stdout conservé à stderr. Cela peut être exagéré, mais avec un peu de chance donne plus de détails sur les descripteurs de fichiers bash (il y a 9 pour chaque processus).
dans Bash, vous pouvez également rediriger vers un sous-puits en utilisant substitution de processus :
command > >(stdlog pipe) 2> >(stderr pipe)
pour la présente affaire:
command 2> >(grep 'something') >/dev/null
combinant le meilleur de ces réponses, si vous le faites:
command 2> >(grep -v something 1>&2)
...alors tout stdout est préservé comme stdout et tout stderr est préservé comme stderr, mais vous ne verrez aucune ligne dans stderr contenant la chaîne"something".
ceci a l'avantage unique de ne pas inverser ou rejeter stdout et stderr, ni les écraser ensemble, ni utiliser des fichiers temporaires.
il est beaucoup plus facile de visualiser les choses si vous pensez à ce qui se passe vraiment avec" redirections "et" pipes."Redirige et pipes dans bash faites une chose: modifiez où les descripteurs de fichier 0, 1 et 2 pointent vers (Voir /proc/[pid]/fd/*).
Lorsqu'un opérateur pipe ou " | " 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 du côté gauche vers ce fifo, et pointe le droit la commande latérale est FD 0 pour le même fifo.
ensuite, les opérateurs de redirection pour chaque côté sont évalués de gauche à droite , et les paramètres actuels sont utilisés chaque fois que la duplication du descripteur se produit. Ceci est important parce que depuis que le tuyau a été mis en place en premier, Le FD1 (côté gauche) et le FD0 (côté droit) sont déjà changés de ce qu'ils auraient pu être normalement, 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:
- un tuyau (fifo) est créé. "commande FD1" est pointé sur ce tuyau. "grep FD0" est aussi pointé vers ce tuyau
- de la commande "FD2" est dirigé à l'endroit où la commande "FD1" actuellement, les points (la pipe)
- de la commande "FD1" est dirigé vers /dev/null
donc, tous sortie que "commande" écrit à son FD 2 (stderr) fait son chemin au tuyau et est lu par "grep" de l'autre côté. Toute la sortie qui" commande " écrit à son FD1 (stdout) fait son chemin vers /dev/null.
si à la place, vous exécutez ce qui suit:
command >/dev/null 2>&1 | grep 'something'
voici ce qui se passe:
- un tuyau est créé et "commande FD 1" et "grep FD 0" sont pointés vers lui
- de la commande "FD 1" est souligné / dev / null
- de la commande "FD 2" est dirigé vers où FD 1 pointe actuellement (/dev/null)
Donc, tous les stdout et stderr de "commande" aller à /dev/null. Rien ne va à la pipe, et donc "grep" va fermer sans rien afficher sur l'écran.
notez Également que des redirections (descripteurs de fichiers) peuvent être en lecture seule (<), en écriture seule (>), ou en lecture-écriture (<>).
un dernier mot. Si un programme écrit quelque chose à FD1 ou FD2, dépend entièrement du programmeur. Les bonnes pratiques de programmation dictent que les messages d'erreur doivent aller à FD 2 et la sortie normale à FD 1, mais vous trouverez souvent une programmation négligée qui mélange les deux ou ignore la convention.
utilisez-vous bash? Dans l'affirmative:
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 les fichiers, grep sur stderr, mais garder le stdout pour écrire des messages à 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
redirigera la commande 1 stderr vers la commande 2 stdin, tout en laissant la commande 1 stdout telle quelle.
exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-
tiré de LDP
je viens de trouver une solution pour envoyer stdout
à une commande et stderr
à une autre, en utilisant des pipes nommées.
voilà.
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 d'enlever les pipes nommées par la suite.
j'essaie de suivre, de trouver du travail en tant que bien,
command > /dev/null 2>&1 | grep 'something'