Filtre à err

Publié le par Peck

Maintenant sur http://linux-attitude.fr/post/Filtre-a-err

Niveau
:
Résumé: exec 3>&1 ; { 2>&1 1>&3; } 3>&1 1>&2 ; exec 3>&-


Aujourd'hui quelques astuces sur les pipes et les redirections en bash. Le but final de cet article étant le filtrage de stderr dans un pipe comme vous le feriez naturellement avec stdout. Vous verrez que ce n'est pas si évident.



Les fondations

Tout d'abord le pipe simple '|' :
$ echo toto | grep -v toto
>

Ajoutons une redirection vers la sortie d'erreur :
$ echo toto 1>&2 | grep -v toto
> toto

1>&2 permet de rediriger stdout sur sterr,


Pour ceux qui prennent le train en marche : 0=stdin (le clavier), 1=stdout (l'ecran), 2=stderr (l'ecran aussi). Le stdin du côté droit du pipe prend toujours sa source du stdout du côté gauche. Le stderr lui, traverse toute la commande d'un coup pour atterrir sur le stderr final.

Notez que le 1 est toujours optionnel dans ces commandes.



Première brique

Tout d'abord apprenons à ouvrir un nouveau file descriptor pour le plaisir.
Celui-ci devra nécessairement pointer sur quelque chose. Donc nous le ferons simplement sortir sur stdout :

$ exec 3>&1
$ echo toto >&3
> toto

Pour fermer ce même file descriptor :
$ exec 3>&-
$ echo toto >&3
> bash: 3: Mauvais descripteur de fichier



Le niveau

Pour la suite nous utiliserons le fichier suivant comme fichier de test, il faut le rendre exécutable et l'appeler test.sh. Il écrit simplement 2 lignes dans stderr et 2 dans stdout.

#!/bin/sh
echo test1 stdout
echo test2 stdout
echo test1 stderr >&2
echo test2 stderr >&2

Exemple simple :
$ ./test.sh | grep test1
> test1 stderr
> test2 stderr
> test1 stdout

Notez que l'ordre de la sortie entre stderr et stdout n'est pas garanti (stderr a en général la priorité).


Illustrons la propagation de stderr sur toute la ligne de commande :
$ { ./test.sh | grep test1; } 2>&1 | grep test2
> test2 stderr

Notez l'utilisation de { ; } (ne pas oublier le ';') qui permet de grouper un ensemble de commandes. L'utilisation de ( ) aurait ici été équivalente, mais aurait provoqué l'exécution d'un sous shell ce qui n'est pas idéal.


Une dernière chose sur les redirections en bash, elles sont évaluées de droite à gauche. Par conséquent la ligne suivant redirige tout vers le fichier toto :
$ ./test.sh >toto 2>&1
>

Alors que celle-ci ne redirige que stdin vers le fichier toto :
$ ./test.sh 2>&1 >toto
> test1 stderr
> test2 stderr



Le produit final

Nous en arrivons enfin à la solution du problème évoqué dans la première phrase. Vous devez maintenant vous douter que nous allons coller tous les morceaux que nous venons de voir ensemble :

$ exec 3>&1
$ { ./test.sh 2>&1 1>&3 | grep test1 ; } 3>&1 1>&2
> test1 stdout
> test2 stdout
> test1 stderr
$ exec 3>&-


Vous venez de filtrer test1 sur stderr, et c'est un vrai filtre car vous pouvez continuer à filtrer sur stdout :

$ exec 3>&1
$ { ./test.sh 2>&1 1>&3 | grep test1 ; } 3>&1 1>&2 | grep test2
> test2 stdout
> test1 stderr
$ exec 3>&-

Publié dans admin

Commenter cet article