« Redirections bash » : différence entre les versions

De knowledge
Aller à la navigation Aller à la recherche
mAucun résumé des modifications
 
(Une version intermédiaire par le même utilisateur non affichée)
Ligne 49 : Ligne 49 :
Donc on voit la ligne en double. D'abord l'echo de ce qu'on tape ensuite la sortie de grep.
Donc on voit la ligne en double. D'abord l'echo de ce qu'on tape ensuite la sortie de grep.


== Redirections ==
== Redirections des flux standards ==
L'un des concepts les puis puissant du shell unix (linux par la suite) est que ces 3 flux peuvent êtres branchées sur n'importe quoi d'autre (on vient d'effleurer le sujet avec ssh).
L'un des concepts les puis puissant du shell unix (linux par la suite) est que ces 3 flux peuvent êtres branchées sur n'importe quoi d'autre (on vient d'effleurer le sujet avec ssh).


=== redirection entre process ===
=== Les types de redirections ===
 
==== redirection entre process ====
c'est la redirection de la sortie d'un process vers l'entrée d'un autre. Ca s'appelle un 'pipe' (un tube, un tuyau... )  
c'est la redirection de la sortie d'un process vers l'entrée d'un autre. Ca s'appelle un 'pipe' (un tube, un tuyau... )  
[[Fichier:Process-bash002.png|sans_cadre|916x916px]]
[[Fichier:Process-bash002.png|sans_cadre|916x916px]]
Ligne 85 : Ligne 87 :
Nous ne sommes pas dans des shell type powershell qui traitent des objets. Ici on traite des flux de caractres est bête et méchants.
Nous ne sommes pas dans des shell type powershell qui traitent des objets. Ici on traite des flux de caractres est bête et méchants.


=== Les types de redirections ===
'''<big>SECTION Pour les experts</big>''' (qui connaissent [[strace]]).
 
'''Vous êtes débutant passez cette section vous y reviendrez plus tard!'''<syntaxhighlight lang="bash">strace -e openat grep "toto" resultat.txt</syntaxhighlight>Nous donne bien une ligne:<syntaxhighlight lang="text">
openat(AT_FDCWD, "resultat.txt", O_RDONLY|O_NOCTTY) = 3
</syntaxhighlight>C'est bien le process "grep" qui ouvre le fichier <code>resultat.txt</code>.
 
Mais si on fait :<syntaxhighlight lang="bash">
cat toto | strace -e openat grep "toto"
</syntaxhighlight>pas de lignes d'ouverture de resultat.txt dans grep. C'est cat qui ouvre le ficher!
 
SI on trace ce que fait cat et pas ce que fait grep:<syntaxhighlight lang="bash">
strace -e openat cat resultat.txt | grep "toto"
</syntaxhighlight>On récupère bien notre ligne<syntaxhighlight lang="text">
openat(AT_FDCWD, "resultat.txt", O_RDONLY) = 3
</syntaxhighlight>Si on fait :<syntaxhighlight lang="bash">
strace -e openat cat < resultat.txt | grep "toto"
</syntaxhighlight>On ne voit plus rien car c'est bash qui ouvre le fichier pour l'envoyer a cat (voir plus loin)
 
'''<big>FIN DE SECTION EXPERTS</big>'''


==== Fichiers ====
==== Fichiers ====
Ligne 125 : Ligne 145 :


'''Tout est simple et logique en bash!'''
'''Tout est simple et logique en bash!'''
==== ">>" Pour pas tout casser ====
La redirection > envoie dans un fichier le contenu de la sortie standard d'un process. Cependant si ce fichier existe il sera écrasé par le contenu en question. Ce n'est pas toujours ce que l'on souhaite!
On veut parfois "ajouter à la fin". <syntaxhighlight lang="bash">
echo "Etat des disques" > status.txt
df -h >> status.txt
</syntaxhighlight>On crée un fichier contenant la ligne "etat des disques" et on y ajoute le résultat de <code>df -h</code>.
J'i mon petit script de création de fichier python vide:<syntaxhighlight lang="bash">
# fichier create-py.sh
echo -n "#!" > $1.py
which python3 >> $1.py
echo "" >> $1.py
echo -n "# " >> $1.py
date "+created on %Y-%m-%d@%H:%M by $USER" >> test.py
chmod a+x $1.py
</syntaxhighlight>On l'appelle avec :<syntaxhighlight lang="bash">./create-py test</syntaxhighlight>... et on a un test.py :<syntaxhighlight lang="python3">
#!/usr/bin/python3
# created on 2026-08-06@01:00 by jpinon
</syntaxhighlight>Correctement configuré pour être lancé directement à la ligne de commande. Il reste plus qu'à....


==== "<" rediriger un fichier vers l'entrée standard d'un process. ====
==== "<" rediriger un fichier vers l'entrée standard d'un process. ====
Ligne 196 : Ligne 238 :
[[Fichier:Process-bash005.png|sans_cadre|916x916px]]
[[Fichier:Process-bash005.png|sans_cadre|916x916px]]


On pourrait même l'envoyer directement à notre amis. Ce fichier code.s.gz j'en ai pas besoin moi!<syntaxhighlight lang="bash">
On pourrait même l'envoyer directement à notre amis. Ce fichier code.s.gz j'en ai pas besoin moi!<syntaxhighlight lang="bash">cc -o - -S hello.c | gzip | mail -s "le code assembleur de hello.c" monami@gmail.com</syntaxhighlight>[[Fichier:Process-bash006.png|sans_cadre|916x916px]]
cc -o - -S hello.c | gzip | mail -s "le code assembleur de hello.c" monami@gmail.com
</syntaxhighlight>[[Fichier:Process-bash006.png|sans_cadre|916x916px]]


Bon comme ça ca parait un peu inutile mais imaginez une tâche [[cron]] qui lance, tous les jours à 4h le matin la commande suivante:<syntaxhighlight lang="bash">
Bon comme ça ca parait un peu inutile mais imaginez une tâche [[cron]] qui lance, tous les jours à 4h le matin la commande suivante:<syntaxhighlight lang="bash">
df -h | mail -s "Etat des disques" moi@mondomaine.fr
df -h | mail -s "Etat des disques" moi@mondomaine.fr
</syntaxhighlight>Et oui! tous les matin j'aurais l'état de remplissage des disque de ma machine dans mes mails. On pourrait améliorer avec un <code>grep</code> entre <code>df</code> et <code>mail</code> pour ne mentionner que les disques qui posent un problème.
</syntaxhighlight>Et oui! tous les matin j'aurais l'état de remplissage des disque de ma machine dans mes mails. On pourrait améliorer avec un <code>grep</code> entre <code>df</code> et <code>mail</code> pour ne mentionner que les disques qui posent un problème.
=== Redirection du flux d'erreur ===
Pour le moment on se traine notre (nos) flux d'erreurs qui vont vers la console. C'est bien car, même quand les sortie standards sont redirigées on peut voir que ça s'est mal passé.
Imaginons que les erreurs suivent le même chemin que la sortie avec:<syntaxhighlight lang="bash">
cc -o - -S hello.c | gzip | mail -s "le code assembleur de hello.c" monami@gmail.com
</syntaxhighlight>imaginons une erreur de compilation (j'ai oublié le int devant le main()):<syntaxhighlight lang="bash">
$ cc -S hello.c
hello.c:3:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
    3 | main(){
      | ^~~~
$
</syntaxhighlight>Dans le cas présent ca m'affiche l'erreur et tout s'arrête après <code>cc</code>. J'aurais mon message et mon ami ne recevra pas de mail.
Si la sortie d'erreur était gérée par le même flux que la sortie standard, je ne m'apercevrais de rien et mon ami recevrait le mail avec le message d'erreur. La loose!
Et bien voila pourquoi la sortie standard est gérée a part.
===== = = = = A FAIRE = = = = =====

Dernière version du 5 août 2026 à 23:25

Les redirections c'est la base des batches !

Echanges standard entre un process et son environnement.

Un process (process ou processus en français j'utiliserais les deux mots indéférament) est une suite d'instructions qui sont exécutées par le système les unes après les autres. Il n'y a pas de notion de parallélisme à ce niveau.

A sa création un process possède trois "flux" standard.

  • Une entrée
  • deux sorties (normal et erreurs)

Si rien n'est précisé l'entrée standard est le clavier et les deux sorties (standard et erreur sont l'écran)

Déjà, de façon, transparente ce clavier et cet écrans sont transformés dans le cas du ssh (ou du telnet). Dans ce cas les entrées sorties sont redirigées vers le réseau lui même ver le client ssh qui... finalement l'envoie à un clavier et un écran même si ce n'est pas celui de ma machine sur laquelle tourne le processus.

Un exemple typique : un simple ls.

$ ls -l
total 44
-rwxr-xr-x 1 jpinon jpinon 15960 Aug  5 21:48 hello
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon   797 Aug  5 21:48 hello.s
-rwxr-xr-x 1 jpinon jpinon 15968 Aug  5 21:50 helloworld
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c
$ ls rrr
ls: cannot access 'rrr': No such file or directory
$

Le premier ls ci dessus envoie sa sortie standard vers l'écran (le flux en bleu sur le schéma). Dans le cas second ls, il s'agit d'une erreur donc vers la sortie d'erreur (flux en rouge).

Il est impossible, pour le moment, de savoir si ce qui est à l'écran a été envoyé vers la sortie standard ou la sortie d'erreur. (sauf le bon sens)

Pour le cas de l'entrée standard l'exemple est plus subtil, on va utiliser grep. grep est un programme assez simple (bon il a plein d'options mais on en parlera pas là) il lit l'entrée standard ligne par ligne et ne l'affiche sur la sortie que si elle comprends un chaine donnée en paramètre.

$ grep toto
Bonjour toi
Bonjour vous
Hello toto
Hello toto
Ca va toto?
Ca va toto?
au revoir toto
au revoir toto
salut

le shell envoie aussi à l'écran ce que l'on tape au clavier avant de l'envoyer au programme (il appelle ca l'écho).

que se passe t'il dans notre exemple? On entre le ligne "Bonjour toi". Le shell l'envoie à l'écran et au process (grep). grep lit la ligne"Bonjour toi"et vérifie si elle contient la chaine qu'on lui a donné en paramètre "toto". Ce n'est pas le cas, grep ignore la ligne. Il se passe la même chose pour "Bonjour vous". En revanche pour "Hello toto" le shell l'envoie a l'écran (echo) et à grep. Grep voit qu'elle contient bien la chaine "toto", alors elle écrit la ligne sur la sortie standard.

Donc on voit la ligne en double. D'abord l'echo de ce qu'on tape ensuite la sortie de grep.

Redirections des flux standards

L'un des concepts les puis puissant du shell unix (linux par la suite) est que ces 3 flux peuvent êtres branchées sur n'importe quoi d'autre (on vient d'effleurer le sujet avec ssh).

Les types de redirections

redirection entre process

c'est la redirection de la sortie d'un process vers l'entrée d'un autre. Ca s'appelle un 'pipe' (un tube, un tuyau... ) Pour chainer ainsi deux process on utilise le caractère "pipe" soit "|". Il s'agit d'une barre verticale qui semble être un tuyau dans le mauvais sens. Il faut connaitre comment les premières machines affichaient ce caractère : pas "|" mais "¦".

Une barre avec un petit trou pour laisser passer le flux. Ce caractère à été simplifié et maintenant et on ne comprends plus bien.

$ ls -l
total 44
-rwxr-xr-x 1 jpinon jpinon 15960 Aug  5 21:48 hello
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon   797 Aug  5 21:48 hello.s
-rwxr-xr-x 1 jpinon jpinon 15968 Aug  5 21:50 helloworld
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c
$ ls -l | grep toto
$ ls -l | grep "c"
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c

La première commande est un bête ls qui donne la liste des fichiers.

La seconde filtre ces lignes pour n'afficher que les lignes contenant la chaine "toto".. c'est à dire aucune. La troisième filtre les lignes pour n'afficher que celles contenant "c" c'est a dire deux d'entres elles.

Attention on parle là de flux de caractères. Les redirections et les process de ces exemples ne "comprennent" pas ce qu'on leur envoie dans les flux.

si on avait utilisé grep avec "n":

$ ls -l | grep "n"
-rwxr-xr-x 1 jpinon jpinon 15960 Aug  5 21:48 hello
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon   797 Aug  5 21:48 hello.s
-rwxr-xr-x 1 jpinon jpinon 15968 Aug  5 21:50 helloworld
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c

Aucun fichier ne contient de "n" mais grep traite le flux ligne de texte par ligne de texte. Il trouve des "n" dans le nom du user et du groupe il affiche la ligne.

Nous ne sommes pas dans des shell type powershell qui traitent des objets. Ici on traite des flux de caractres est bête et méchants.

SECTION Pour les experts (qui connaissent strace).

Vous êtes débutant passez cette section vous y reviendrez plus tard!

strace -e openat grep "toto" resultat.txt

Nous donne bien une ligne:

openat(AT_FDCWD, "resultat.txt", O_RDONLY|O_NOCTTY) = 3

C'est bien le process "grep" qui ouvre le fichier resultat.txt. Mais si on fait :

cat toto | strace -e openat grep "toto"

pas de lignes d'ouverture de resultat.txt dans grep. C'est cat qui ouvre le ficher! SI on trace ce que fait cat et pas ce que fait grep:

strace -e openat cat resultat.txt | grep "toto"

On récupère bien notre ligne

openat(AT_FDCWD, "resultat.txt", O_RDONLY) = 3

Si on fait :

strace -e openat cat < resultat.txt | grep "toto"

On ne voit plus rien car c'est bash qui ouvre le fichier pour l'envoyer a cat (voir plus loin)

FIN DE SECTION EXPERTS

Fichiers

">" Rediriger la sortie standard vers un fichier

Autant "|" dirige la sortie d'un processus vers l'entrée d'un autre processus ">" redirige simplement la sortie standard d'un process vers un fichier disque.

Exemple simple un simple ls.

$ ls -l
total 44
-rwxr-xr-x 1 jpinon jpinon 15960 Aug  5 21:48 hello
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon   797 Aug  5 21:48 hello.s
-rwxr-xr-x 1 jpinon jpinon 15968 Aug  5 21:50 helloworld
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c

La commande ls envoie la liste des fichier vers la sortie standard. Jusque là on n'a rien fait!

$ ls -l > resultat.txt

Cette commande ne réponds rien et c'est normal. On lui a demandé de rediriger la sortie standard vers un fichier "resultat.txt" Si on refait un ls.

$ ls -l
total 48
-rwxr-xr-x 1 jpinon jpinon 15960 Aug  5 21:48 hello
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon   797 Aug  5 21:48 hello.s
-rwxr-xr-x 1 jpinon jpinon 15968 Aug  5 21:50 helloworld
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c
-rw-r--r-- 1 jpinon jpinon   344 Aug  5 23:21 resultat.txt

Effectivement on a un nouveau fichier! SI on regarde ce qu'il i a dedans (cat, vi, nano... chacun sa méthode) mais on y trouve :

-rwxr-xr-x 1 jpinon jpinon 15960 Aug  5 21:48 hello
-rw-r--r-- 1 jpinon jpinon    84 Aug  5 21:48 hello.c
-rw-r--r-- 1 jpinon jpinon   797 Aug  5 21:48 hello.s
-rwxr-xr-x 1 jpinon jpinon 15968 Aug  5 21:50 helloworld
-rw-r--r-- 1 jpinon jpinon    63 Aug  5 21:50 helloworld.c
-rw-r--r-- 1 jpinon jpinon     0 Aug  5 23:21 resultat.txt

On remarque que "resultat.txt" est déjà dedans mais avec une taille nulle. Etrange? pas tant que ça et ca nous montre bien comment ça se passe.

Lors de la commande "ls -l > resultat.txt" le shell (bash ici) voit que la sortie de ls devra aller dans un fichier "resultat.txt" qu'il s'empresse de créer mais, pour le moment, il est vide. Le shell lance alors le "ls -l" qui fait la liste des fichiers, dont resultat.txt qui est vide, et l'envoie dans le fichier "resultat.txt" qui se remplis alors.

A la sortie resultat.txt est donc plein mais, quand il a été lut par ls il était vide!

Tout est simple et logique en bash!

">>" Pour pas tout casser

La redirection > envoie dans un fichier le contenu de la sortie standard d'un process. Cependant si ce fichier existe il sera écrasé par le contenu en question. Ce n'est pas toujours ce que l'on souhaite!

On veut parfois "ajouter à la fin".

echo "Etat des disques" > status.txt
df -h >> status.txt

On crée un fichier contenant la ligne "etat des disques" et on y ajoute le résultat de df -h. J'i mon petit script de création de fichier python vide:

# fichier create-py.sh
echo -n "#!" > $1.py
which python3 >> $1.py
echo "" >> $1.py
echo -n "# " >> $1.py
date "+created on %Y-%m-%d@%H:%M by $USER" >> test.py
chmod a+x $1.py

On l'appelle avec :

./create-py test

... et on a un test.py :

#!/usr/bin/python3
# created on 2026-08-06@01:00 by jpinon

Correctement configuré pour être lancé directement à la ligne de commande. Il reste plus qu'à....

"<" rediriger un fichier vers l'entrée standard d'un process.

C'est la partie la moins élégante de la syntaxe je trouve mais bon. On peut utiliser un fichier comme l'entrée d'un process.

C'est assez rarement utilisé parce que'à la fois pas clair et pas très utile. Suivez moi bien:

cat

affiche le résultat de l'entrée standard sur la sortie standard. Le truc le plus inutile possible!

$ cat
hello
hello
bonjour
bonjour
jaquot
jaquot
blabla
blabla

Ca répète tout ce qu'on tape jusqu'à la fin du texte (EOF ou <Ctrl> D) si on voulait lui faire écrire ce qu'il y a dans un fichier on sait qu'il faut faire :

cat resultat.txt

Ca marche mais c'est pas ce que je veux vous montrer. C'est juste le fonctionnement de la commande cat. SI on lui donne un paramètre au lieu de copier l'entrée standard elle prends le paramètre en question, ouvre le fichier disque qui correspond de l'envoie sur la sortie standard. Là c'est le programme cat qui fait le travail. Si on fait :

cat < resultat.txt # Attention un < s'est introduit dans cette commande!

le résultat est exactement le même mais les actions effectuées sont totalement différentes!

Que fait le shell?

  • Il ouvre cat sans paramètres qui va alors lire les lignes une par une sur l'entrée standard et les envoyer sur la sortie standard jusqu'à EOF. Il est bête mais on lui en demande pas plus.
  • Mais le shell vois aussi que l'entrée standard de cat doit être "alimentée", non pas par le clavier mais par le contenu d'un fichier "resultat.txt"
  • doc cat prends ce qu'il a en entrée (le shell lui envoie le contenu de resultat.txt) et l'envoie sur la sortie standard (ici l'écran)

On pourrait envisager un:

cat < resultat.txt > resultat1.txt

...qui en fait est l'équivalant d'une copie de resultat.txt vers resultat1.txt.

cp resultat1 resultat2

aurait fait pareil. Encore plus pervère

cat < resultat.txt | grep jpinon > resultat2.txt

Le shell traite comme ça:

  • ouverture de cat "tout nu" qui lit sur l'entrée et envoie vers la sortie
  • son entrée est connectée par shell sur le fichier resultat.txt. Ok, donc cat ne le sait pas mais il lit les lignes de resultat.txt une par une (c'est le shell qui les lui envoie).
  • cat ecrit vers sa sortie standars mais le shell la redirrige vers grep.
  • grep est presque aussi bête que cat. Il lit des lignes sur son entrée standard et les sort sur la sur la sortie standard pour peu que la ligne contienne la chaine en paramètre
  • mais le shell est malin, il lui envoie sur son entrée standard la sortie de cat.
  • a la fin le shell détourne la sortie de grep vers resultat2.txt

Tout ca se résume dans :

Remarque sur l'usage de "-"

Quelquefois il est indispensable de donner un fichier à une commande. dans ce cas là on peut remplacer ne nom du fichier par "-".

Par exemple :

cat -

est équivalant à:

cat

bon pas très utile. Mais pensons au compilateur cc. une option est de lui demander de juste s'arreter au code assembleur mais pas d'aller jusqu'au code machine.

cc -S hello.c

va, par défaut nous sortir un fichier hello.s qui contient le code assembleur. Imaginons que l'on veuille avoir ce code assembleur pour l'envoyer à un ami mais on le voudrait "gzippé". Problème cc sait sortir dans un fichier texte, on peut lui donner le nom du fichier de sortie par l'option "-o fichier.s" mais il ne sait pas zipper. On porait faire ça en deux commandes :

cc -o code.s -S hello.c
gzip code.s

en sortie on aura un fichier code.s.gz tres bien mais on a appris a faire des redirections! Alors on en fait!

cc -o - -S hello.c | gzip > code.s.gz

C'est pas beau!

Avec un beau dessin :

On pourrait même l'envoyer directement à notre amis. Ce fichier code.s.gz j'en ai pas besoin moi!

cc -o - -S hello.c | gzip | mail -s "le code assembleur de hello.c" monami@gmail.com

Bon comme ça ca parait un peu inutile mais imaginez une tâche cron qui lance, tous les jours à 4h le matin la commande suivante:

df -h | mail -s "Etat des disques" moi@mondomaine.fr

Et oui! tous les matin j'aurais l'état de remplissage des disque de ma machine dans mes mails. On pourrait améliorer avec un grep entre df et mail pour ne mentionner que les disques qui posent un problème.

Redirection du flux d'erreur

Pour le moment on se traine notre (nos) flux d'erreurs qui vont vers la console. C'est bien car, même quand les sortie standards sont redirigées on peut voir que ça s'est mal passé.

Imaginons que les erreurs suivent le même chemin que la sortie avec:

cc -o - -S hello.c | gzip | mail -s "le code assembleur de hello.c" monami@gmail.com

imaginons une erreur de compilation (j'ai oublié le int devant le main()):

$ cc -S hello.c
hello.c:3:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
    3 | main(){
      | ^~~~
$

Dans le cas présent ca m'affiche l'erreur et tout s'arrête après cc. J'aurais mon message et mon ami ne recevra pas de mail.

Si la sortie d'erreur était gérée par le même flux que la sortie standard, je ne m'apercevrais de rien et mon ami recevrait le mail avec le message d'erreur. La loose!

Et bien voila pourquoi la sortie standard est gérée a part.

= = = = A FAIRE = = = =