Redirection et pipes
Les redirections des E/S permettent d’assembler des processus en pipeline.
1. Entrées/sorties standard
De base, tout processus dispose de 3 flux de communication :
-
1 flux d’entrée stdin (descripteur 0) pour l’acquisition de données, affecté par défaut au clavier,
-
2 flux de sortie : stdout (descripteur 1) pour la sortie des résultats, et stderr (descripteur 2) pour l’envoi des messages d’erreur, tous deux affectés par défaut à l’écran.
Ainsi :
-
read linelit une ligne depuisstdin, -
echo $lineécrit une ligne surstdout, et -
echo $line >/dev/stderrécrit une ligne surstderr.
| Un processus peut ouvrir d’autres flux, en plus de ces 3 flux parv défaut, pour par exemple lire ou écrire des données depuis ou dans des fichiers. |
Par exemple, en exécutant :
$ find / -type d -name lib
find: ‘/boot/efi’: Permission non accordée
find: ‘/boot/grub2’: Permission non accordée
/tmp/lib
on constate que find sort 2 types de messages :
-
ceux correspondant aux répertoires trouvés
-
ceux correspondant aux interdictions
2. Redirection
C’est l’opération consistant à modifier les affectations ci-dessus.
2.1. Redirection de l’entrée standard
$ less < /etc/passwd
$ read line < /etc/passwd
$ echo $line
root:x:0:0:root:/root:/bin/bash
2.2. Redirection de la sortie standard
$ ls > /tmp/ls.out # écrase /tmp/ls.out s'il existait
$ ls / >> /tmp/ls.out # ajoute à la fin de /tmp/ls.out s'il existe, le crée sinon
$ find / -type d -name lib > /dev/null
find: ‘/boot/efi’: Permission non accordée
find: ‘/boot/grub2’: Permission non accordée
2.3. Redirection de la sortie d’erreur standard
$ grep -R unix /etc 2>/tmp/grep.err # écrase /tmp/grep.err s'il existait
$ grep -R unix /etc 2>>/tmp/grep.err # n'écrase pas /tmp/grep.err s'il existait
$ find / -type d -name lib 2>/dev/null
/tmp/lib
2.4. Combiner les redirections
On peut mixer tout ça, afin de rediriger plusieurs flux pour un même processus :
$ grep -R unix /etc >/tmp/grep.out 2>/tmp/grep.err # redirige [1] et [2]
$ grep bin < /etc/passwd > /tmp/bin.lines # redirige [0] et [1]
2.5. Autres possibilités
D’autres redirections sont possibles, comme rediriger stderr vers stdout.
$ grep -R unix /etc >/tmp/grep.out 2>&1
2.6. /dev/tty et /dev/null
Ce sont 2 fichiers périphériques particuliers :
$ ls -l /dev/null /dev/tty
crw-rw-rw-. 1 root root 1, 3 27 déc. 09:51 /dev/null
crw-rw-rw-. 1 root tty 5, 0 27 déc. 09:51 /dev/tty
/dev/tty correspond à la console courante.
Lire depuis le fichier /dev/tty revient à lire sur le clavier.
Ecrire sur /dev/tty revient à écrire sur l’écran.
| Exemple d’utilisation : dans une commande dont la sortie a été redirigée vers un fichier, vouloir malgré tout écrire sur l’écran |
/dev/null est un trou noir.
Lire sur /dev/null renvoie aussitôt une fin de fichier.
De même, tout ce qui part sur /dev/null est perdu.
/dev/null est souvent utilisé pour faire disparaitre des information non souhaitées :
|
$ grep -il linux /etc/* 2>/dev/null
3. Pipe
C’est une chaîne de processus dont les flux d’E/S sont interconnectés 2 à 2.
$ ps aux | grep $USER | less
$ ps aux | grep $USER > /tmp/ps.out
4. Filtres
Un filtre est une commande destinée à agir sur le flux de données qui la traverse.
Les données arrivent sur stdin et ressortent modifiées sur stdout :
$ ./un_filtre <entree >sortie
Il s’intercale dans un pipe :
$ ./premier | ./un_filtre | ./dernier
La plupart des commandes Unix lisent leurs données au choix sur l’entrée standard ou depuis un fichier.
Exemple de la commande less :
si on lui fournit un argument, alors less le considère comme le nom d’un fichier à visualiser,
et s’il n’y a pas d’argument, alors less visualise ce qui lui arrive sur stdin :
$ less /etc/passwd # données lues depuis un fichier
$ less < /etc/passwd # données lues depuis stdin
$ ls -l | less # données lues depuis stdin
Donc souvent, les commandes Unix se comportent comme un filtre lorsqu’on ne leur fournit pas le nom de fichier qu’elles attendent. Exemple :
$ grep root /etc/passwd # grep lisant ses données depuis un fichier
$ ps aux | grep $USER | less # grep utilisé comme filtre
5. Commandes anodines
Les commandes historiques d’Unix sont souvent simples. Elles suivent généralement le principe suivant : faire une chose précise, sans chercher à en faire trop (« Do one thing and do it well »).
Voici donc quelques commandes anodines de manipulation de texte. Malgré leur simplicité apparente, elle peuvent s’avèrer extrêmement utiles lorsqu’on les combine entre elles.
5.1. L’exemple de cat
cat est l’une des commandes les plus simples qu’il soit.
consulter le contenu d’un fichier
$ cat /etc/passwd
consulter le contenu de plusieurs fichiers
$ cat /etc/passwd /etc/group
concaténer plusieurs fichiers
Par conséquent, on peut l’utiliser pour concaténer plusieurs fichiers (d’où son nom) :
$ cat /etc/passwd /etc/group > /tmp/concat
Si on ne lui fournit pas d’argument, alors elle recopie sur sa sortie standard ce qui arrive sur son entrée stanbdard. C’est une fonctionnalité vraiment toute bête. Mais cela permet de remplir un fichier à partir du clavier sans utiliser un éditeur de texte :
créer un fichier
$ cat > /tmp/test (1)
un
deux
trois
^D
| 1 | stdin affecté au clavier,
stdout redirigé vers /tmp/test |
ou de le compléter :
compléter un fichier
$ cat >> /tmp/concat
un
deux
trois
^D
numéroter les lignes
$ cat -n /etc/services
$ ls | cat -n
5.2. wc
wc (word count) renvoie le nombres de caractères, de mots et de lignes d’un flux.
C’est un filtre utile pour compter.
Par exemple, combien y a-t-il de fichiers dans son homedir :
$ find ~ -type f | wc -l
5.3. more - less
more est la commande historique, less en est une amélioration.
Ces 2 commandes paginent un flux.
$ ps -edf | less
5.4. grep
grep filtre les lignes d’un flux.
grep s’intercale fréquemment dans un pipe pour éliminer des lignes :
$ ps -edf | grep $USER | less
5.5. awk
awk est l’exception de cette liste, car elle n’est pas anodine,
c’est une puissante commande de traitement de texte.
Aujourd’hui, elle n’est plus indispensable
(awk est le précurseur du langage Perl qui l’a supplanté),
mais sa faculté à découper une ligne de texte
la rend encore utile au quotidien.
$ ps aux | awk '{print $1,$2,$11}'
USER PID COMMAND
root 1 /usr/lib/systemd/systemd
root 980 /usr/lib/systemd/systemd-journald
root 1019 /usr/lib/systemd/systemd-nsresourced
root 1020 /usr/lib/systemd/systemd-userdbd
root 1036 /usr/lib/systemd/systemd-udevd
rpc 1226 /usr/bin/rpcbind
systemd+ 1234 /usr/lib/systemd/systemd-oomd
systemd+ 1236 /usr/lib/systemd/systemd-resolved
$ awk -F : '{print $1," -> ",$5}' /etc/passwd
root -> Super User
bin -> bin
daemon -> daemon
adm -> adm
lp -> lp
sync -> sync
shutdown -> shutdown
halt -> halt
mail -> mail
operator -> operator
games -> games
ftp -> FTP User
nobody -> Kernel Overflow User
5.6. sort
sort trie les lignes d’un flux (par ordre alphabétique par défaut) :
$ sort /etc/passwd | head -7
abrt:x:173:173::/etc/abrt:/sbin/nologin
adm:x:3:4:adm:/var/adm:/usr/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
backuppc:x:976:976::/var/lib/BackupPC:/sbin/nologin
bin:x:1:1:bin:/bin:/usr/sbin/nologin
chrony:x:997:995:chrony system user:/var/lib/chrony:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/usr/sbin/nologin
Par exemple, avoir la liste de ses répertoires personnels par taille décroissante :
$ du -d1 ~ | sort -rn
14269060 /home/jaclin
11658832 /home/jaclin/Documents
219080 /home/jaclin/.mozilla
192776 /home/jaclin/.local
61424 /home/jaclin/Téléchargements
24784 /home/jaclin/tmp
2944 /home/jaclin/Images
56 /home/jaclin/bin
32 /home/jaclin/.ssh
24 /home/jaclin/.bashrc.d
4 /home/jaclin/Desktop
0 /home/jaclin/Vidéos
ou avec des tailles plus parlante :
$ du -hd1 ~ | sort -rh
14G /home/jaclin
12G /home/jaclin/Documents
214M /home/jaclin/.mozilla
189M /home/jaclin/.local
60M /home/jaclin/Téléchargements
25M /home/jaclin/tmp
2,9M /home/jaclin/Images
56K /home/jaclin/bin
32K /home/jaclin/.ssh
24K /home/jaclin/.bashrc.d
4,0K /home/jaclin/Desktop
0 /home/jaclin/Vidéos
5.7. uniq
uniq garde 1 seul exemplaire d’une suite de lignes consécutives identiques
(et peut compter les exemplaires regroupés).
Par exemple :
$ cat f
un
un
deux
un
trois
trois
trois
trois
deux
deux
$ cat f | uniq -c
2 un
1 deux
1 un
4 trois
2 deux
Par exemple, sortir la liste des utilisateurs associés à leur nombre de processus, triée par nombre de processus :
$ ps aux | sort | awk '{print $1}' | uniq -c | sort -n
1 chrony
1 colord
1 geoclue
1 polkitd
1 rtkit
1 USER
2 avahi
2 dbus
2 postfix
2 systemd+
120 jaclin
265 root
5.8. tail - head
Extrait les dernières/premières lignes d’un flux.
baba$ grep baba /usr/share/dict/words | head
Ababa
baba
babacoote
babai
babajaga
baba-koto
babakoto
babas
babasco
babassu
baba$ grep baba /usr/share/dict/words | tail
babasco
babassu
babassus
babasu
babaylan
babaylanes
Barbabas
Cayubaba
Cayubaban
Mbabane
$ du -h ~ | sort -h | tail -3
367M /home/jaclin/.config
12G /home/jaclin/Documents
14G /home/jaclin
A noter l’option -f de tail, qui lui permet de rester en attente d’arrivée de nouvelles lignes dans le ou les fichiers spécifiés.
Très utile pour surveiller les logs.
$ tail -f /var/log/messages
6. Heredoc
C’est une construction syntaxique permettant d’envoyer des lignes de texte sur stdin,
sans créer de fichier :
$ ssh data << EOT
whoami
hostname
pwd
EOT
Cette écriture est pratique dans un script, car elle évite de créer un fichier de données à part. La commande ci-dessus est équivalente aux 2 commandes ci-dessous :
$ cat > /tmp/cmd
whoami
hostname
pwd
^D
$ ssh data < /tmp/cmd