103.4 Leçon 2
Certification : |
LPIC-1 (101) |
---|---|
Version : |
5.0 |
Thème : |
103 Commandes GNU et Unix |
Objectif : |
103.4 Utiliser les flux, les tubes et les redirections |
Leçon : |
2 sur 2 |
Introduction
Un des aspects de la philosophie Unix stipule que chaque programme doit avoir une fonction spécifique et ne pas essayer d’incorporer des fonctionnalités en dehors de son champ d’application. Le fait de garder les choses simples ne signifie pas pour autant que les résultats seront moins élaborés, étant donné que différents programmes peuvent s’enchaîner pour produire un résultat combiné. La barre verticale |
, également connu sous le nom de symbole pipe (ou "tube"), peut être utilisé pour créer un pipeline qui relie la sortie d’un programme directement à l’entrée d’un autre programme, tandis que la substitution de commande permet de stocker la sortie d’un programme dans une variable ou de l’utiliser directement comme argument d’une autre commande.
Les tubes
Contrairement aux redirections, les tubes font circuler les données de gauche à droite dans la ligne de commande. La cible est un autre processus et non pas un chemin du système de fichiers, un descripteur de fichier ou un document en ligne (Here document
). Le caractère pipe |
indique au shell de lancer toutes les commandes distinctes en même temps et de connecter la sortie de la commande précédente à l’entrée de la commande suivante, de gauche à droite. Par exemple, au lieu d’utiliser des redirections, le contenu du fichier /proc/cpuinfo
envoyé vers la sortie standard par cat
peut être redirigé vers l’entrée standard de wc
avec la commande suivante :
$ cat /proc/cpuinfo | wc 208 1184 6096
En l’absence d’un chemin vers un fichier, wc
compte le nombre de lignes, de mots et de caractères qu’il reçoit sur son entrée standard, comme c’est le cas dans l’exemple. Plusieurs pipes peuvent être présents dans une commande combinée. L’exemple suivant utilise deux pipes :
$ cat /proc/cpuinfo | grep 'model name' | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
Le contenu du fichier /proc/cpuinfo
produit par cat /proc/cpuinfo
a été envoyé vers la commande grep 'model name'
, qui sélectionne alors uniquement les lignes contenant le terme model name
. La machine qui exécute l’exemple a beaucoup de processeurs, il y a donc plusieurs lignes avec model name
. Le dernier tube relie grep 'model name'
à uniq
, qui se charge de supprimer toute ligne égale à la précédente.
Les tubes peuvent être combinés avec les redirections dans la même ligne de commande. L’exemple précédent peut être réécrit plus simplement :
$ grep 'model name' </proc/cpuinfo | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
La redirection d’entrée pour grep
n’est pas strictement nécessaire puisque grep
accepte un chemin de fichier comme argument, mais l’exemple montre comment on peut construire ce genre de commandes combinées.
Les tubes et les redirections fonctionnent de manière exclusive, c’est-à-dire qu’une source ne peut être mise en correspondance qu’avec une seule cible. Pourtant, il est possible de rediriger une sortie vers un fichier tout en l’affichant à l’écran avec le programme tee
. Pour ce faire, le premier programme envoie sa sortie vers l’entrée standard de tee
et un nom de fichier est fourni à ce dernier pour stocker les données :
$ grep 'model name' </proc/cpuinfo | uniq | tee cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz $ cat cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
Le résultat du dernier programme de la chaîne, généré par uniq
, est affiché et stocké dans le fichier cpu_model.txt
. Pour éviter d’écraser le contenu du fichier fourni et pour y ajouter des données, l’option -a
doit être fournie à tee
.
Seule la sortie standard d’un processus est capturée par un tube. Imaginons que vous devez suivre un processus de compilation prolongé à l’écran tout en enregistrant à la fois la sortie standard et l’erreur standard dans un fichier pour une inspection ultérieure. En supposant que votre répertoire courant ne possède pas de Makefile, la commande suivante va générer une erreur :
$ make | tee log.txt make: *** No targets specified and no makefile found. Stop.
Bien qu’affiché à l’écran, le message d’erreur généré par make
n’a pas été capturé par tee
et un fichier log.txt
vide a été créé. Il faut donc effectuer une redirection avant qu’un tube ne puisse capturer l’erreur standard :
$ make 2>&1 | tee log.txt make: *** No targets specified and no makefile found. Stop. $ cat log.txt make: *** No targets specified and no makefile found. Stop.
Dans cet exemple, l’erreur standard de make
a été redirigée vers la sortie standard, de sorte que tee
a pu la capturer avec un tube, l’afficher à l’écran et l’enregistrer dans le fichier log.txt
. Dans des cas comme celui-ci, il peut être utile de sauvegarder les messages d’erreur pour une inspection ultérieure.
La substitution de commande
Une autre méthode pour capturer la sortie d’une commande, c’est la substitution de commande. En plaçant une commande entre des apostrophes inversées, Bash la remplace par sa sortie standard. L’exemple suivant montre comment utiliser la sortie standard d’un programme comme argument d’un autre programme :
$ mkdir `date +%Y-%m-%d` $ ls 2019-09-05
La sortie du programme date
, la date actuelle au format année-mois-jour, a été utilisée comme argument pour créer un répertoire avec mkdir
. Un résultat identique est obtenu en utilisant $()
au lieu des apostrophes inversées :
$ rmdir 2019-09-05 $ mkdir $(date +%Y-%m-%d) $ ls 2019-09-05
La même méthode peut être utilisée pour stocker la sortie d’une commande sous forme de variable :
$ OS=`uname -o` $ echo $OS GNU/Linux
La commande uname -o
affiche le nom générique du système d’exploitation utilisé, lequel est stocké dans la variable de session OS
. L’affectation de la sortie d’une commande à une variable est très utile dans les scripts, car elle permet de stocker et d’évaluer les données de plusieurs manières différentes.
En fonction de la sortie générée par la commande remplacée, la substitution de commande intégrée peut ne pas être appropriée. Une méthode plus sophistiquée pour utiliser la sortie d’un programme comme argument d’un autre programme fait appel à un intermédiaire appelé xargs
. Le programme xargs
utilise le contenu qu’il reçoit via l’entrée standard pour exécuter une commande donnée avec le contenu comme argument. L’exemple suivant montre xargs
en train d’exécuter le programme identify
avec les arguments fournis par le programme find
:
$ find /usr/share/icons -name 'debian*' | xargs identify -format "%f: %wx%h\n" debian-swirl.svg: 48x48 debian-swirl.png: 22x22 debian-swirl.png: 32x32 debian-swirl.png: 256x256 debian-swirl.png: 48x48 debian-swirl.png: 16x16 debian-swirl.png: 24x24 debian-swirl.svg: 48x48
Le programme identify
fait partie de ImageMagick, une suite d’outils en ligne de commande pour inspecter, convertir et retoucher la plupart des types de fichiers images. Dans l’exemple, xargs
a pris tous les chemins listés par find
et les a mis comme arguments à identify
, qui affiche alors les informations pour chaque fichier avec le format requis par l’option -format
. Les fichiers trouvés par find
dans l’exemple sont des images contenant le logo de la distribution dans un système de fichiers Debian. -format
est un paramètre de identify
, et non pas de xargs
.
L’option -n 1
demande à xargs
d’exécuter la commande donnée avec un seul argument à la fois. Dans le cas de l’exemple, au lieu de passer tous les chemins trouvés par find
comme une liste d’arguments à identify
, l’utilisation de xargs -n 1
exécutera la commande identify
séparément pour chaque chemin. L’utilisation de -n 2
exécutera identify
avec deux chemins comme arguments, -n 3
avec trois chemins comme arguments et ainsi de suite. De même, lorsque xargs
traite des contenus multilignes — comme c’est le cas avec l’entrée fournie par find
— l’option -L
peut être utilisée pour limiter le nombre de lignes qui seront utilisées comme arguments par l’exécution de la commande.
Note
|
L’utilisation de |
Si les chemins contiennent des espaces, il est primordial de lancer find
avec l’option -print0
. Cette option indique à find
d’utiliser un caractère vide entre chaque entrée de manière à ce que la liste puisse être correctement analysée par xargs
(la sortie a été supprimée) :
$ find . -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 du | sort -n
L’option -0
demande à xargs
d’utiliser le caractère vide comme séparateur. Ainsi, les chemins d’accès aux fichiers fournis par find
sont correctement analysés, même s’ils contiennent des espaces ou d’autres caractères spéciaux. L’exemple précédent montre l’utilisation de la commande du
qui permet de connaître l’utilisation du disque pour chaque fichier trouvé, puis de trier les résultats par taille. La sortie a été supprimée pour plus de concision. Notez que pour chacun des critères de recherche, il est nécessaire de passer l’option -print0
à find
.
Par défaut, xargs
place les arguments de la commande exécutée en dernier. Pour changer ce comportement, il faut utiliser l’option -I
:
$ find . -mindepth 2 -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 -I PATH mv PATH ./
Dans le dernier exemple, chaque fichier trouvé par find
est déplacé vers le répertoire courant. Comme le ou les chemins source doivent être communiqués à mv
avant le chemin cible, un terme de substitution est donné à l’option -I
de xargs
qui est ensuite placé de manière appropriée juste après mv
. En utilisant le caractère vide comme séparateur, il n’est pas nécessaire de mettre le terme de substitution entre guillemets.
Exercices guidés
-
Il est pratique d’enregistrer la date d’exécution des actions effectuées par des scripts automatisés. La commande
date +%Y-%m-%d
affiche la date actuelle au format année-mois-jour. Comment peut-on stocker le résultat de cette commande dans une variable shell appeléeTODAY
en utilisant la substitution de commande ? -
En utilisant la commande
echo
, comment peut-on envoyer le contenu de la variableTODAY
vers l’entrée standard de la commandesed s/-/./g
? -
Comment la sortie de la commande
date +%Y-%m-%d
peut-elle être utilisée comme chaîne de caractères en ligne (Here string) pour la commandesed s/-/./g
? -
La commande
convert image.jpeg -resize 25% small/image.jpeg
crée une version réduite deimage.jpeg
et place l’image résultante dans un fichier de même nom dans le sous-répertoiresmall
. En utilisantxargs
, comment peut-on exécuter la même commande pour chaque image listée dans le fichierfilelist.txt
?
Exercices d’approfondissement
-
Une routine de sauvegarde simple génère régulièrement une image de la partition
/dev/sda1
avecdd < /dev/sda1 > sda1.img
. Pour effectuer des vérifications ultérieures de l’intégrité des données, la routine génère également une empreinte SHA1 du fichier avecsha1sum < sda1.img > sda1.sha1
. En ajoutant des tubes et la commandetee
, comment ces deux commandes pourraient-elles être combinées en une seule ? -
La commande
tar
est utilisée pour archiver plusieurs fichiers au sein d’un seul fichier, tout en préservant la structure des répertoires. L’option-T
permet de spécifier un fichier contenant les chemins à archiver. Par exemple,find /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -
crée un fichier tar compresséetc.tar.xz
à partir de la liste fournie par la commandefind
(l’option-T -
indique l’entrée standard comme liste de chemins). Afin d’éviter d’éventuelles erreurs d’analyse dues à des chemins contenant des espaces, quelles options de commande doivent être présentes pourfind
ettar
? -
Au lieu d’ouvrir une nouvelle session shell distante, la commande
ssh
permet tout simplement d’exécuter une commande spécifiée en argument :ssh user@storage "remote command"
. Étant donné quessh
permet également de rediriger la sortie standard d’un programme local vers l’entrée standard du programme distant, comment la commandecat
pourrait-elle faire passer un fichier local nomméetc.tar.gz
vers/srv/backup/etc.tar.gz
sur le systèmeuser@storage
viassh
?
Résumé
Cette leçon aborde les techniques traditionnelles de communication inter-processus utilisées par Linux. Le pipelining de commande crée un canal de communication unidirectionnel entre deux processus et la substitution de commande permet de stocker la sortie d’un processus dans une variable shell. La leçon passe en revue les points suivants :
-
Comment les tubes peuvent être utilisés pour transmettre la sortie d’un processus à l’entrée d’un autre processus.
-
L’utilité des commandes
tee
etxargs
. -
Comment capturer la sortie d’un processus avec la substitution de commande, en la stockant dans une variable ou en l’utilisant directement comme paramètre d’une autre commande.
Voici les procédures et les commandes abordées :
-
Le pipelining de commande avec
|
. -
La substitution de commande avec des apostrophes inversées et
$()
. -
Les commandes
tee
,xargs
etfind
.
Réponses aux exercices guidés
-
Il est pratique d’enregistrer la date d’exécution des actions effectuées par des scripts automatisés. La commande
date +%Y-%m-%d
affiche la date actuelle au format année-mois-jour. Comment peut-on stocker le résultat de cette commande dans une variable shell appeléeTODAY
en utilisant la substitution de commande ?$ TODAY=`date +%Y-%m-%d`
ou bien
$ TODAY=$(date +%Y-%m-%d)
-
En utilisant la commande
echo
, comment peut-on envoyer le contenu de la variableTODAY
vers l’entrée standard de la commandesed s/-/./g
?$ echo $TODAY | sed s/-/./g
-
Comment la sortie de la commande
date +%Y-%m-%d
peut-elle être utilisée comme chaîne de caractères en ligne (Here string) pour la commandesed s/-/./g
?$ sed s/-/./g <<< `date +%Y-%m-%d`
ou bien
$ sed s/-/./g <<< $(date +%Y-%m-%d)
-
La commande
convert image.jpeg -resize 25% small/image.jpeg
crée une version réduite deimage.jpeg
et place l’image résultante dans un fichier de même nom dans le sous-répertoiresmall
. En utilisantxargs
, comment peut-on exécuter la même commande pour chaque image listée dans le fichierfilelist.txt
?$ xargs -I IMG convert IMG -resize 25% small/IMG < filelist.txt
ou bien
$ cat filelist.txt | xargs -I IMG convert IMG -resize 25% small/IMG
Réponses aux exercices d’approfondissement
-
Une routine de sauvegarde simple génère régulièrement une image de la partition
/dev/sda1
avecdd < /dev/sda1 > sda1.img
. Pour effectuer des vérifications ultérieures de l’intégrité des données, la routine génère également une empreinte SHA1 du fichier avecsha1sum < sda1.img > sda1.sha1
. En ajoutant des tubes et la commandetee
, comment ces deux commandes pourraient-elles être combinées en une seule ?# dd < /dev/sda1 | tee sda1.img | sha1sum > sda1.sha1
-
La commande
tar
est utilisée pour archiver plusieurs fichiers au sein d’un seul fichier, tout en préservant la structure des répertoires. L’option-T
permet de spécifier un fichier contenant les chemins à archiver. Par exemple,find /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -
crée un fichier tar compresséetc.tar.xz
à partir de la liste fournie par la commandefind
(l’option-T -
indique l’entrée standard comme liste de chemins). Afin d’éviter d’éventuelles erreurs d’analyse dues à des chemins contenant des espaces, quelles options de commande doivent être présentes pourfind
ettar
?Les options
-print0
et--null
:$ find /etc -type f -print0 | tar -cJ -f /srv/backup/etc.tar.xz --null -T -
-
Au lieu d’ouvrir une nouvelle session shell distante, la commande
ssh
permet tout simplement d’exécuter une commande spécifiée en argument :ssh user@storage "remote command"
. Étant donné quessh
permet également de rediriger la sortie standard d’un programme local vers l’entrée standard du programme distant, comment la commandecat
pourrait-elle faire passer un fichier local nomméetc.tar.gz
vers/srv/backup/etc.tar.gz
sur le systèmeuser@storage
viassh
?$ cat etc.tar.gz | ssh user@storage "cat > /srv/backup/etc.tar.gz"
ou bien
$ ssh user@storage "cat > /srv/backup/etc.tar.gz" < etc.tar.gz