Les redirections permettent de faire communiquer des applications entre elles.
Vous allez faire différentes manipulation et surtout observer ce qui se passe.
Toute commande exécutée s’affichera dans le terminal courant.
Par exemple ici, la commande ls -lA
affiche:
total 12
-rw-r--r--. 1 supv supv 18 8 nov. 2019 .bash_logout
-rw-r--r--. 1 supv supv 141 8 nov. 2019 .bash_profile
-rw-r--r--. 1 supv supv 312 8 nov. 2019 .bashrc
La commande touch
permet de créer un fichier vide (ou de
changer sa date de modification, comme le manuel peut l’expliquer).
Taper les commandes suivantes:
$ touch bonjour.txt
$ cat bonjour.txt
Que constatez-vous ?
Nous allons maintenant écrire dans le fichier grâce au caractère
>
. Ce caractère redirige la sortie de la commande qui le
précède vers un fichier.
$ echo Bonjour le monde
Bonjour le monde
$ echo Bonjour le monde > bonjour.txt
La commande cat
sert à afficher dans la sortie standard
le contenu d’un fichier:
$ cat bonjour.txt
Bonjour le monde
Il est possible de rediriger vers un fichier qui ne stocke aucune information.
C’est le fichier spécial /dev/null
: l’équivalent d’un
trou noir duquel rien de ce qui entre ne ressort.
$ echo Bonjour le monde > /dev/null
$
Il ne se passe rien et c’est normal.
Tapez la commande suivante:
$ rm hello.txt > /dev/null
rm: hello.txt: No such file or directory
Cette fois la redirection semble ne pas avoir marché puisque quelque chose s’est affiché.
En fait, la redirection a bien eu lieu. Mais un programme a une deuxième sortie: la sortie d’erreur.
Par défaut, cette sortie est aussi redirigée vers le terminal courant:
Elle permet à un processus de communiquer les problèmes qu’il peut rencontrer au cours de son exécution, même si sa sortie standard est redirigée vers un fichier ou un autre programme.
Tapez la commande suivante:
$ ls -l hello.txt bonjour.txt
ls: hello.txt: No such file or directory
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
Une erreur est survenue ici: un des fichiers est absent.
Tapez la commande suivante, qui ne provoque pas d’erreur :
$ ls -l bonjour.txt
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
Et maintenant, la commande suivante:
$ ls -l hello.txt bonjour.txt > /dev/null
ls: hello.txt: No such file or directory
Cette fois, seule l’erreur est affichée.
Maintenant, nous allons la faire disparaître en la redirigeant vers
/dev/null
en utilisant 2>
:
$ ls -l hello.txt bonjour.txt 2> /dev/null
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
Puis tout faire disparaître en la redirigeant tout vers
/dev/null
en utilisant 2>
et
>
:
$ ls -l hello.txt bonjour.txt > /dev/null 2> /dev/null
Qu’en pensez-vous ? Faites un schéma qui récapitule ce qui s’est passé pour chaque commande.
Exemple d’utilisation courante de /dev/null
grep network /etc/*
La commande précédente affiche les messages d’erreurs à côté des
messages d’information de grep
Pour supprimer les messages d’erreur:
grep network /etc/* 2> /dev/null
Pour afficher uniquement les messages d’erreur:
grep network /etc/* > /dev/null
Nous allons maintenant rediriger vers des fichiers:
$ ls -l hello.txt bonjour.txt > fichier
ls: hello.txt: No such file or directory
Puis taper la commande cat fichier
: que constatez-vous
? est-ce normal ?
$ ls -l hello.txt bonjour.txt 2> erreur
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
$
Que contient le fichier erreur
?
Maintenant, nous allons combiner les deux :
$ ls -l hello.txt bonjour.txt > fichier 2> erreur
Ce qui suit donne des informations sur les commandes précédentes.
Expérience 1 - Créer le fichier
bonjour.txt
:
$ touch bonjour.txt
Expérience 2 - Supprimer le fichier
hello.txt
(qu’il existe ou non):
$ rm hello.txt > /dev/null
Expérience 3 - Tenter de lister les deux fichiers :
$ ls -l bonjour.txt hello.txt
ls: cannot access 'hello.txt': No such file or directory
-rw-rw-r-- 1 laurent laurent 8 févr. 27 12:12 bonjour.txt
Expérience 4 - La même commande en redirigeant le
flux standard (stdout
) vers sortie
:
$ ls -l bonjour.txt hello.txt > sortie
ls: cannot access 'hello.txt': No such file or directory
Expérience 5 - La même commande en redirigeant le
flux d’erreur (stderr
) vers erreur
:
$ ls -l bonjour.txt hello.txt 2> erreur
-rw-rw-r-- 1 laurent laurent 8 févr. 27 12:12 bonjour.txt
Expérience 6 - Affichage du contenu du fichier
erreur
:
$ cat erreur
ls: cannot access 'hello.txt': No such file or directory
Expérience 7 - Affichage du contenu du fichier
erreur
:
$ cat sortie
-rw-rw-r-- 1 laurent laurent 8 févr. 27 12:12 bonjour.txt
Expérience 8 - Redirection des deux flux de sortie dans des fichiers:
$ ls -l bonjour.txt hello.txt 2> erreur 1> sortie
Expérience 9 - Affichage des contenus des deux fichiers:
$ cat erreur
ls: cannot access 'hello.txt': No such file or directory
$ cat sortie
-rw-rw-r-- 1 laurent laurent 8 févr. 27 12:12 bonjour.txt
Expérience 10 - Concaténation des deux flux de sortie dans des fichiers :
$ ls -l bonjour.txt hello.txt >& sortie-et-erreur
Exécuter les commandes suivantes:
echo '# A propos' > README.md
echo 'Je suis un fichier informatif' >> README.md
echo 'Ma création a été réalisée en plusieurs opérations' >> README.md
Afficher le contenu de ce fichier.
Exécuter la commande suivante:
cat README.md bonjour.txt
Comment faire pour que ce contenu soit directement enregistré dans un
fichier nommé BONJOUR.md
?
Il est possible de donner la sortie d’un programme en entrée d’un autre programme.
C’est une des fonctionnalités majeures d’UNIX/Linux : on peut créer une transformation complexe à partir de transformations simples.
|
|&
redirige stderr
vers
stdout
La commande suivante permet d’afficher les informations réseau:
ip a
Il est possible de ne récupérer qu’une partie des lignes avec les commandes suivantes:
ip a | head -1
ip a | tail -1
Essayer ces commandes en remplaçant -1
par
-2
, -3
, etc
La commande grep
permet de sélectionner
certaines lignes :
ip a | grep inet
La commande awk
permet (entre autre) de sélectionner les
champs d’une lignes :
ip a | awk '{print $1}'
Essayer cette commande en remplaçant $1
par
$2
, etc
On peut donc chaîner ces commandes:
ip a | grep inet | tail -n 1 | awk '{ print $2 }'
Et ajouter autant d’opérations que souhaité:
ip a | grep inet | tail -n 1 | awk '{ print $2 }' | cut -d/ -f1
Essayer cette commande en remplaçant -f1
par
-f2
.
cut
$ echo "1,2,3,4,5"
1,2,3,4,5
$ echo "1,2,3,4,5" | cut -d, -f2
2
$ echo "un,deux,trois,quatre,cinq" | cut -d, -f2
deux
$ echo "un,deux, trois,quatre ,cinq" | cut -d, -f2
deux
$ echo "un,deux, trois,quatre ,cinq" | cut -d, -f3-
trois,quatre ,cinq
$ echo "un,deux, trois,quatre ,cinq" | cut -d, -f2-4
deux, trois,quatre
$ echo "un,deux, trois,quatre ,cinq" | cut -d" " -f2
trois,quatre
$ echo " un deux trois quatre" | cut -d" " -f2
un
Bien noter la différence de comportement par rapport à
awk
:
$ echo " un deux trois quatre" | awk '{print $2}'
deux
Utilisation de la substitution de commande (command substitution)
Utilisation du backtick ```:
$ adresse_ip=`ip a | grep ens | tail -n 1 | awk '{print $2}' | awk -F/ '{print $1}'`
$ echo $adresse_ip
192.168.21.11
Utilisation de la substitution de commande $(...)
:
$ adresse_ip=$(ip a | grep ens | tail -n 1 | awk '{print $2}' | awk -F/ '{print $1}')
$ echo $adresse_ip
192.168.21.11
La commande suivante affiche de nombreuses informations.
$ ls -l
total 16
-rw-rw-r-- 1 ian ian 0 déc. 7 14:47 adaptable
-rw-rw-r-- 1 ian ian 0 déc. 7 14:48 cap
lrwxrwxrwx 1 ian ian 6 déc. 8 14:17 lien_vers_ancien -> ancien
Si nous souhaitons afficher la dernière partie (la colonne affichant les noms de fichier), on va chercher à supprimer les caractères du début de la ligne.
Pour cela, on va commencer par compter ces caractères (commande
wc
) :
$ echo "-rw-rw-r-- 1 ian ian 0 déc. 7 14:47 " | wc -c
43
Puis, à l’aide de la commande cut
, on peut désormais
découper chaque ligne affichée:
$ ls -l | cut -c43-
adaptable
cap
lien_vers_ancien -> ancien
Lancer les commandes suivantes:
sleep 2001 &
sleep 2002 &
Ouvrir un nouveau terminal (en gardant le précédent ouvert) et une nouvelle session sur votre machine.
Exécuter la commande suivante:
$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 S laurent 1613 1612 0 80 0 - 5240 - 11:15 pts/1 00:00:00 -bash
0 R laurent 1698 1613 0 80 0 - 9577 - 11:33 pts/1 00:00:00 ps -lf
En exécutant la commande ps -elf
, la sortie est beaucoup
plus verbeuse. Exécuter la commande suivante et trouver la
commande sleep 2001
précédemment lancée:
ps -elf | less
Pour que la commande suivante fonctionne correctement, il est
nécessaire de relancer les commandes sleep
qui auraient été
tuées précédemment.
Pour tuer cette commande, on peut lancer la commande
kill
suivie du numéro de processus (PID). Cependant, on
peut automatiser cela ainsi:
ps -elf | grep 'sleep 2001' |grep -v grep | awk '{print $4}' | xargs kill
Evidemment, cette commande est dangereuse: elle
permet de tuer tous les processus correspondant au paramètre du premier
grep
.
Lancer et analyser la commande:
ps -elf | grep 'sleep' |grep -v grep | awk '{print $4}'
Que font chacune des opérations ?
Que se passe-t-il si on ajoute une commande xargs kill
à
la fin ?
Lancer la commande suivante:
echo 'Bonjour les gens, comment allez-vous ?' | tr 'a-z' 'A-Z'
Que permet de faire la commande tr
?
echo 'Bonjour les gens, comment allez-vous ?' | tr 'a-z' 'A-Z'
echo 'Bonjour les gens, comment allez-vous ?' | tr 'a-z' 'A-Z' | rev
echo 'Bonjour les gens, comment allez-vous ?' | tr 'a-z' 'A-Z' | rev | awk '{$1="!!!" ; print}'
echo 'Bonjour les gens, comment allez-vous ?' | tr 'a-z' 'A-Z' | rev | awk '{$1="!!!" ; print}' | rev
Les file descriptors (ou fd) permettent de définir des emplacement de redirection.
Il y a trois fd
par défaut:
On redirige en utilisant les opérateurs de redirection
(>
ou 2>
) vers un fd.
Un fd est noté: &n
où n
est le numéro
du fd
. Par exemple, &1
désigne
standard output
:
$ ls -l hello.txt bonjour.txt > sortie.log 2>&1
$ cat sortie.log
ls: hello.txt: No such file or directory
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
L’ordre a de l’importance:
$ ls -l hello.txt bonjour.txt 2>&1 > sortie.log
ls: hello.txt: No such file or directory
$ cat sortie.log
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
Voici un code à placer dans un fichier nommé demoredir
:
echo "Ci-dessous: la valeur du fichier de sortie stdout:"
readlink $(readlink /dev/stdout)
echo "Ci-dessous: la valeur du fichier de sortie stderr:"
readlink $(readlink /dev/stderr)
echo "Un message standard"
echo "Un message d'erreur" >&2 # &2 = File Descriptor n°2
# 2>&1
# 1>&2 est équivalent à >&2
Exécuter la commande chmod +x demoredir
Observons ce qui se passe à l’exécution:
$ ./demoredir > essairedir 2>&1
$ cat essairedir
Ci-dessous: la valeur du fichier de sortie stdout:
/home/laurent/essairedir
Ci-dessous: la valeur du fichier de sortie stderr:
/home/laurent/essairedir
Un message standard
Un message d'erreur
$ ./demoredir > essairedir
Un message d'erreur
$ cat essairedir
Ci-dessous: la valeur du fichier de sortie stdout:
/home/laurent/essairedir
Ci-dessous: la valeur du fichier de sortie stderr:
/dev/pts/2
Un message standard
less
Pour afficher les messages d’erreur dans less
, il faut
donc penser à rediriger la sortie d’erreur dans la sortie standard:
$ ls -l hello.txt bonjour.txt 2>&1 | less
Syntaxe simplifiée:
ls -l bonjour.txt hello.txt |& less
La commande tee
afficher l’entrée standard (standard
input) qu’elle reçoit vers la sortie standard et vers
le fichier donné en paramètre:
$ ls -l hello.txt bonjour.txt | tee output.log
ls: hello.txt: No such file or directory
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
$ cat output.log
-rw-r--r-- 1 lauhub staff 17 26 mar 11:02 bonjour.txt
Il est même possible d’ajouter des données avec tee
:
$ ls -l /usr/bin/awk | tee -a output.log
lrwxrwxrwx 1 root root 21 Jul 2 13:47 /usr/bin/awk -> /etc/alternatives/awk
$ cat output.log
-rw-r--r-- 1 laurent laurent 8 Jul 3 12:04 bonjour.txt
lrwxrwxrwx 1 root root 21 Jul 2 13:47 /usr/bin/awk -> /etc/alternatives/awk
Faites en sorte que l’exécution de la commande ls -l
/usr/bin/nmt*
:
information.log
Faites en sorte que l’exécution de la commande ls -l hello.txt
bonjour.txt
:
information.log
Vous pouvez passer cette partie si vous le souhaitez: dans un premier temps, les informations qui y sont données ne sont pas indispensables.
fd
du
shell courantL’astuce consiste à utiliser la variable spéciale $$
qui
contient l’identifiant du processus courant:
ls -l /proc/$$/fd
fd
Ouvrir un fd se fait en utilisant la commande exec
exec 3<> /tmp/testfd
Il s’utilise ensuite comme les fd
par défaut
echo bonjour >&3
echo je suis ici >&3
echo je suis encore la >&3
Il est possible (et recommandé) de le fermer quand il n’est plus utile.
exec 3>&-
Que devrait afficher la commande suivante ?
cat /tmp/testfd
Que peut-on en conclure ?
Si on souhaite filtrer la sortie d’erreur pour en récupérer une partie :
$ ls hello.txt bonjour.txt
ls: cannot access 'hello.txt': No such file or directory
bonjour.txt
Le filtre est opéré par défaut sur la sortie standard (n°1):
$ ls hello.txt bonjour.txt | awk -F: '{print $3}'
ls: cannot access 'hello.txt': No such file or directory
$
Pour inverser la sortie on va utiliser un fd intermédiaire:
$ ls hello.txt bonjour.txt 3>&1 1>&2 2>&3 | awk -F: '{print $3}'
bonjour.txt
No such file or directory
$
3>&1
: redirige une nouvelle sortie vers la
sortie 1 (stdout)1>&2
: redirige la sortie standard (1 =
stdout) vers la sortie d’erreur (2 = stderr)2>&3
: redirige la sortie 2 (stderr)
vers la sortie 3 (qui a été redirigée vers stdout en 1)Par exemple, créer le fichier texte script.sh
avec le
contenu suivant:
echo "Quel est ton prénom ? "
read
prenom=$REPLY
echo "Quel est ton nom ? "
read
nom=$REPLY
echo "Donne-moi ton âge: "
read age
echo "Salut $prenom $nom, tu as $age ans."
Pour exécuter ce script, au moins deux possibilités.
La première en appelant la commande bash
suivie du nom
du script :
bash script.sh
Il est possible de créer un fichier permettant d’automatiser les entrées au clavier pour un programme.
Nous allons maintenant rendre le fichier script.sh
exécutable (nous verrons plus en détail les droits et permissions dans
un autre chapitre):
chmod +x script.sh
Pour exécuter le script, il suffit maintenant d’appeler la commande suivante:
./script.sh
./
permet de dire que le script se trouve explicitement
dans le dossier courant. Ceci est nécessaire car il n’est pas dans un
chemin pris en charge par la variable PATH
.
Voici un exemple d’exécution du script:
$ bash script.sh
Quel est ton prénom ?
Juan
Quel est ton nom ?
Formell
Donne-moi ton âge:
70
Salut Juan Formell, tu as 70 ans.
Créer un fichier contenant les réponses à donner au script. Ici, les réponses à donner sont les suivantes:
Juan
)Formell
)70
)Voici le contenu du fichier correspondant
(reponses.txt
):
Juan
Formell
70
Il est possible de donner les réponses au script directement par une redirection entrante:
$ ./script.sh < reponses.txt
Quel est ton prénom ?
Quel est ton nom ?
Donne-moi ton âge:
Salut Juan Formell, tu as 70 ans.
Ici, c’est le fichier reponses.txt
qui a remplacé les
entrées faites au clavier par l’utilisateur.
Un Here Document (ou heredoc) est un fichier créé à la volée et qui permet de fournir les entrées à un programme de manière automatisée.
Il commence en général par :
<<
ou
<<-
)EOF
)Il se termine par le délimiteur choisi.
Exemple:
$ ./script.sh <<EOF
Laurent
HUBERT
17
EOF
Ce qui donne à l’exécution:
$ ./script.sh <<EOF
> Laurent
> HUBERT
> 17
> EOF
Quel est ton prénom ?
Quel est ton nom ?
Donne-moi ton âge:
Salut Laurent HUBERT, tu as 17 ans.
Il est possible également d’indenter le texte. C’est le rôle du
marqueur <<-
(deux chevrons suivis d’un tiret).
Ceci est utile dans un script. Par exemple, ici le contenu du fichier
auto.sh
(on utilise des tabulations au début des trois
lignes données en réponse):
./script.sh <<-FINDEFLUX
Laurent
HUBERT
17
FINDEFLUX
L’exécution de ce script donnera:
$ bash auto.sh
Quel est ton prénom ?
Quel est ton nom ?
Donne-moi ton âge:
Salut Laurent HUBERT, tu as 17 ans.
Créer un fichier et le rendre exécutable après y avoir placé le contenu suivant:
nom_du_programme=$0
cat <<EOF
Bonjour $USER,
Je suis content de t'accueillir sur ce merveilleux système nommé GNU/Linux.
C'est un héritier glorieux de UNIX
Tu viens d'exécuter un script nommé "$nom_du_programme"
EOF
Exécuter ce fichier, constater.
Créer un fichier et le rendre exécutable après y avoir placé le contenu suivant:
nom_du_programme=$0
cat <<-EOF
Bonjour $USER,
Je suis content de t'accueillir sur ce merveilleux système nommé GNU/Linux.
C'est un héritier glorieux de UNIX. Il permet de bien gérer le décalage des marges (indentation).
Tu viens d'exécuter un script nommé "$nom_du_programme"
EOF
Exécuter ce fichier, constater.
Créer un fichier et le rendre exécutable après y avoir placé le contenu suivant:
nom_du_programme=$0
cat <<-'EOF'
Bonjour $USER,
Je suis content de t'accueillir sur ce merveilleux système nommé GNU/Linux.
C'est un héritier glorieux de UNIX. Ici les variables ne sont pas interprétées...
Tu viens d'exécuter un script nommé "$nom_du_programme"
EOF
On aurait pu utiliser des double-quotes, avec le même effet:
cat <<-"EOF"
$je_ne_suis_pas_interpretee
EOF
Exécuter ce fichier, constater.