dernière modification : 2023

Redirection et pipes

Les redirections des E/S permettent d’assembler des commandes en un pipeline.

1. Entrées/sorties standard

Chaque 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.

redirect1

Par exemple, si le processus est un script bash, alors :

  • read line lit une ligne depuis stdin,

  • echo $line écrit une ligne sur stdout, et

  • echo $line >/dev/stderr écrit une ligne sur stderr.

Si le processus est un script Python, alors :

  • input(line) lit une ligne depuis stdin,

  • print(line) écrit une ligne sur stdout, et

  • print(line,file=sys.stderr) écrit une ligne sur stderr.

Le processus peut ouvrir d’autres flux, pour par exemple lire ou écrire des données depuis ou dans d’autres fichiers.

2. Redirection

C’est l’opération consistant à modifier les affectations ci-dessus.

2.1. Redirection de l’entrée standard

redirect2
exemples de 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

redirect3
exemple 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

2.3. Redirection de la sortie d’erreur standard

redirect4
exemple 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

2.4. Combiner les redirections

On peut mixer tout ça, afin de rediriger plusieurs flux pour un même processus :

redirect5
exemple
$ 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.

redirect6
exemple
$ 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, et tout ce qui part sur /dev/null disparait.

$ 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.

pipe1
$ 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 :

schéma d’utilisation d’un filtre
$ ./un_filtre <entree >sortie

On l’utilise souvent en l’intercalant dans un pipe :

filtre dans un pipe
$ ./premier | ./un_filtre | ./dernier

Généralement, les commandes Unix lisent leurs données au choix sur la ligne de commande, sur l’entrée standard ou depuis un fichier, et envoyent leurs données au choix dans un fichier ou sur la sortie standard. Par exemple pour 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

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

Lorsqu’une commande a besoin qu’on lui fournisse un nom de fichier à l’appel, alors (très souvent) on peut lui en fournir plusieurs.

$ grep root /etc/passwd /etc/group

Une commande doit accepter un nombre quelconque de fichiers pour pouvoir utiliser des noms fichier génériques :

$ grep root /etc/x* (1)
1 grep reçoit ici en argument tous les noms de fichiers s’identifiant à /etc/x*

Le traitement de la commande ci-dessus est le suivant :

  1. le shell récupère la ligne "grep root /etc/x*"

  2. il l’interpole, ce qui donne :

    grep root /etc/x2go /etc/xdg /etc/xml /etc/xscreensaver
  3. puis il lance la commande grep en lui passant comme arguments :

    root /etc/x2go /etc/xdg /etc/xml /etc/xscreensaver

On voit bien que pour que cette dernière étape aboutisse, il faut que grep accepte en argument un nombre quelconque de noms de fichiers.

4.1. L’exemple de cat

cat pour consulter le contenu de fichiers
$ cat /etc/passwd /etc/group
cat pour créer un fichier
$ cat > /tmp/test (1)
un
deux
trois
^D
1 stdin redirigé vers le clavier, stdout redirigé vers /tmp/test
cat pour concaténer plusieurs fichiers
$ cat /etc/passwd /etc/group > /tmp/concat
cat pour compléter un fichier
$ cat >> /tmp/concat
un
deux
trois
^D

4.2. tee

tee est un dupliqueur de flux. Les données issues de stdin ressortent de tee à la fois sur stdout et dans les fichiers dont les noms lui sont donnés.

tee
$ find /usr -name '*lib*' | tee /tmp/find.out | less

5. 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