Être à l'aise avec bash #4
Résumé des épisodes précédents :
- Le prompt bash et la manipulation des titres des fenêtres
- Navigation dans les répertoires en ligne de commande et les alias
- L'historique et le complètement avancé avec bash
Après ces trois billets plutôt sur la configuration de bash, passons aux choses sérieuses avec l'étude de trois concepts qui font du shell en général et de bash en particulier un outil extrêment puissant :
- Les gestion des entrées/sorties (ou flux ou Input/Output ou I/O pour les intimes :) avec les redirections et l'utilisation du pipe.
- Rediriger la sortie standard dans un fichier
- Rediriger la sortie d'erreur dans un fichier
- Rediriger la sortie standard et la sortie d'erreur sur la sortie standard
- Et l'entrée standard ?
- Pour aller plus loin
- Les motifs (ie le globbing)
Les entrées/sorties
L'article sur les flux standards sur Wikipedia est une très bonne et courte introduction au concept d'entrée/sorties standards. Pour faire simple, lorsqu'un shell est lancé en mode interactif (ie le shell attend qu'on lui tape des commandes typiquement dans un terminal), l'entrée standard est le clavier et la sortie standard et la sortie d'erreur sont reliées au terminal. Par défaut, lorsque le shell lance une commande, cette commande hérite (entre autre) des entrées/sorties standards du shell parent. Ces flux sont manipulables dans le shell, quelques exemples pratiques à l'aide de la commande find permettant de rechercher tous les répertoires dans le dossier de l'utilisateur root sur lequel mon utilisateur n'a évidemment pas tous les droits, cette commande renvoie donc des informations sur la sortie d'erreur et la sortie standard :
$ find ~root -type d
/root
/root/.gnome2
find: /root/.gnome2: Permission non accordée
/root/.gconf
find: /root/.gconf: Permission non accordée
Rediriger la sortie standard dans un fichier
Cette opération se fait avec l'opérateur >, qui créa le fichier si il n'existe pas ou l'écrasera avec la sortie standard en l'utilisant de la manière suivante :
$ find ~root -type d > sortie_standard
find: /root/.gnome2: Permission non accordée
find: /root/.gconf: Permission non accordée
$ cat sortie_standard
/root
/root/.gnome2
/root/.gconf
Plutôt que d'écraser le fichier, il est aussi possible d'ajouter la sortie au fichier si celui-ci existe déjà avec l 'opérateur >>.
Rediriger la sortie d'erreur dans un fichier
Cette opération se fait également avec > mais en spécifiant le descripteur de la sortie d'erreur (par défaut 2) :
$ find ~root -type d 2> sortie_erreur
/root
/root/.gnome2
/root/.gconf
$ cat sortie_erreur
find: /root/.gnome2: Permission non accordée
find: /root/.gconf: Permission non accordée
Cette séquence est fréquemment utilisée pour n'afficher que la sortie standard en spécifiant comme fichier le fichier spécial /dev/null. De la même manière que précédemment, 2>> ajoutera la sortie d'erreur à la fin du fichier.
Rediriger la sortie d'erreur et la sortie standard sur la sortie standard
Ici, on souhaite tout avoir sur la même sortie pour un traitement ultérieur, pour cela il faut indiquer au shell de rediriger les données écrites sur le descripteur 2 sur le descripteur 1.
$ find ~root -type d > sortie_1_et_2 2>&1
$ cat sortie_1_et_2
/root
/root/.gnome2
find: /root/.gnome2: Permission non accordée
/root/.gconf
find: /root/.gconf: Permission non accordée
Et l'entrée standard ?
Il est possible d'enchâiner les commandes en redirigeant les sorties d'une commande sur l'entrée standard à l'aide du pipe (| ou tube), par exemple si je souhaite n'avoir que les messages concernant les dossier de GNOME à la suite de find, je peux utiliser l'utilitaire grep pour filtrer la sortie comme suit :
$ find ~root -type d 2>&1 | grep gnome
/root/.gnome2
find: /root/.gnome2: Permission non accordée
Comme vue précédemment, les sorties standard et d'erreur de find sont redirigés sur la sortie standard qui est elle même redirigées sur l'entrée standard de grep. Cette ligne est équivalente aux deux lignes suivantes utilisant le symbole < permettant de rediriger le contenu d'un fichier sur l'entrée standard :
$ find ~root -type d > sortie_1_et_2 2>&1
$ grep gnome < sortie_1_et_2
/root/.gnome2
find: /root/.gnome2: Permission non accordée
elle même équivalente à
$ find ~root -type d > sortie_1_et_2 2>&1
$ cat sortie_1_et_2 | grep gnome
/root/.gnome2
find: /root/.gnome2: Permission non accordée
Ce sont des exemples très simples, mais il est possible ainsi de faire des choses assez complexe en une ligne en enchaînant quelques commandes (qui feront l'objet d'un prochaine billet) :
$ cat access.log | egrep -v '(/var|/design|/share|/stats| 404 )' | cut -d ' ' -f 7 | sort | uniq | wc -l
2911
Cette ligne lit le fichier de log d'apache access.log et permet de compter le nombre de pages différentes vues au moment où j'écris ces lignes.
Pour aller plus loin
Pour en revenir aux entrée/sorties, ces opérations sont les plus courantes, mais il existe bien d'autres possibilités décrites dans la page du manuel de bash.
Les motifs
Les motifs sont des caractères spécifiques qui permettent, une fois développé par le shell, de remplacer plusieurs fichiers sans avoir à tous les écrire. Pour faire simple, ce sont des expressions rationnelles très limitées. Le plus connu de ces caractères est l'étoile * souvent utilisé dans les rm -rf * (ce qui est très dangereux !). Pour tester ces caractères, il vaut mieux utiliser echo.
À la suite des commandes tapées ci-dessus, j'ai plusieurs fichiers dans le répertoire temporaire qui peuvent servir d'exemple :
$ ls
gconfd-tigrou mapping-tigrou sortie_1_et_2 sortie_standard
listen orbit-tigrou sortie_erreur ssh-jDyKBu4304
# tout ce qui commençe par un s
$ echo s*
sortie_1_et_2 sortie_erreur sortie_standard ssh-jDyKBu4304
# tout ce qui termine par un u
$ echo *u
gconfd-tigrou mapping-tigrou orbit-tigrou
# tout ce qui termine par un chiffre
$ echo *[0-9]
sortie_1_et_2 ssh-jDyKBu4304
# tout ce qui ne se termine PAS par un chiffre
$ echo *[!0-9]
gconfd-tigrou listen mapping-tigrou orbit-tigrou sortie_erreur sortie_standard
# tout ce qui commence par sortie suivi d'un caractère suivi de standard
$ echo sortie?standard
sortie_standard
Pour utiliser l'un de ces caractères sur la ligne de commande sans qu'il soit interprèté, il suffit de la préfixer d'un antislash ().