Aller au contenu

Administration Unix - CM séance 02

3. Manipulations simples de contenus

3.1. Entrées/sorties

Un programme en cours d'exécution s'appelle un processus (le programme peut être une commande, un shell, un éditeur de texte, ...).

Chaque processus possède sa propre liste de fichiers ouverts :

  • le numéro d'un fichier dans la liste s'appelle le descripteur du fichier ;
  • cette liste s'appelle la table des descripteurs du processus.

Un processus dispose de trois fichiers pour les entrées/sorties :

  • l'entrée standard (descripteur 0)
  • la sortie standard (descripteur 1)
  • la sortie d'erreurs (descripteur 2)

Par défaut :

  • l'entrée standard est le terminal (qui lit le clavier)
  • les sorties standard et d'erreurs sont dirigées vers le terminal (qui affiche ce qu'il reçoit).

3.2. Redirection vers un fichier

Le shell permet de rediriger une entrée ou une sortie avec les symboles < ou > lorsqu'on entre une commande.

Pour rediriger l'entrée standard :

$ commande -options < fichier

ou encore

$ commande -options 0< fichier

Ceci exécutera la commande en redirigeant son entrée standard (0) depuis le fichier :

  • lorsque la commande lira son entrée standard, elle lira en réalité dans le fichier ;
  • la commande ne saura pas qu'elle lit un fichier !

Pour rediriger la sortie standard (1) :

$ commande -options > fichier1

ou encore

$ commande -options 1> fichier1

Le fichier1 sera créé ou écrasé.

Exemple

$ echo -e "porte\nsous\nalarme" > idesecours    # crée le fichier
$ cat idesecours                                # affiche le fichier
porte
sous
alarme
$ sort < idesecours                             # fichier en entrée
alarme
porte
sous

Il peut y avoir une erreur lors de l'écrasement :

$ echo > toto       # crée fichier
$ echo > toto       # l'écrasement provoque une erreur
bash: toto : impossible d'écraser le fichier existant

$ set +C            # on autorise l'écrasement
$ echo > toto       # écrasement ok

$ set -C            # on revient au mode par défaut
$ echo > toto       # écrasement interdit
bash: toto : impossible d'écraser le fichier existant

$ echo >| toto      # syntaxe pour forcer l'écrasement (sans utiliser set +C)

On peut aussi rediriger en ajout :

$ commande -options >> fichier1

ou encore

$ commande -options 1>> fichier1

Le fichier1 sera créé ou ouvert en ajout à la fin (mode append).

Pour rediriger la sortie d'erreur (2) :

$ commande -options 2> fichier2

Exemple

Exemple typique avec une commande qui fait des erreurs :

$ echo "ga tor" > ali ; mkdir alu
$ grep tor al*
ali:ga tor                      # ligne trouvée par grep
grep: alu: est un dossier       # message d'erreur de grep

$ grep tor al* 2> erreurs.txt
ali:ga tor                      # on ne voit que les lignes trouvées
$ cat erreurs.txt
grep: alu: est un dossier       # les messages d'erreur sont dans le fichier
$ rm -f erreurs.txt

$ grep tor al* 2> /dev/null     # envoie les erreurs dans la poubelle
ali:ga tor

On peut demander plusieurs redirections :

$ commande -options < fichier0 > fichier1 2> fichier2

idem avec les variantes >> >| 1>> 1>| 2>> 2>|

3.3. Redirection vers un descripteur

Il est possible de rediriger un descripteur vers le fichier pointé par un autre descripteur, sans repréciser le fichier. La syntaxe est :

  • a>&b en écriture,
  • a<&b en lecture ;

ceci redirige le descripteur a vers le fichier pointé par le descripteur b.

De cette façon on peut rediriger 2 vers le fichier pointé par 1 :

$ cmd 1> fichier1 2>&1

ou encore rediriger 1 vers le fichier pointé par 2 :

$ cmd 2> fichier2 1>&2

⚠  L'ordre dans lequel on fait les redirections est important :

$ cmd 1> fichier1 2>&1

redirigera 1 vers le fichier1, puis 2 vers le fichier pointé par 1 qui est fichier1 :

alors que

$ cmd 2>&1 1> fichier1

redirigera 2 vers le fichier pointé par 1 qui est le terminal, puis 1 vers le fichier1 :

3.4. Liaison entre 2 commandes avec un tube

Une des possibilités qui a fait la puissance du shell a été de relier la sortie standard d'une commande vers l'entrée standard d'une autre commande.

Le caractère qui permet d'indiquer cela est le | (pipe, ou tube) :

$ cmd1 -options | cmd2 -options

Exemple :

$ ps -aux | grep bash

ps -aux affiche dans le tube la liste de tous les processus, grep bash lit les lignes dans le tube et affiche celles qui ont le mot bash.

Il est possible d'enchaîner les tubes, on appelle cela un pipeline :

$ cmd1 -options | cmd2 -options | cmd3 -options ...

Voici le diagramme des redirections de ce pipeline :

On peut préciser l'entrée de la première commande et la sortie de la dernière

$ cmd1 -options < fichier1 | ... | cmdn -options > fichiern

En fait on peut rediriger les entrées-sorties de chacune des commandes du pipeline, par exemple :

cmd1 2>> fichier1 | cmd2 2>&1 | cmd3 2> /dev/null

Le diagramme des redirections est alors

Approfondissement

Pour réaliser le pipeline

$ cmd1 | cmd2

que fait bash en interne ?

  • bash crée un tube (appel de la fonction C pipe) ; un tube consiste en

    • un buffer mémoire (généralement de 1M),
    • une position de début et de fin dans le buffer,
    • un descripteur pour écrire dans le buffer (remplir),
    • un descripteur pour lire dans le buffer (vider) ;
  • bash crée 2 processus (appels C fork) ;

  • bash redirige les entrées-sortie des processus sur les descripteurs du tube (appel de la fonction C dup2) ;
  • bash recouvre les processus avec les commandes cmd1 et cmd2 (appels C execvp) ;

  • cmd1 écrit dans le tube jusqu'à ce qu'il soit plein (write), puis cmd1 est endormi par le système, et cmd2 est réveillé ;

  • cmd2 lit dans le tube jusqu'à ce qu'il soit vide (read), puis cmd2 est endormi par le système, et cmd1 est réveillé ;
  • et ainsi de suite jusqu'à ce que cmd1 ferme son extrémité du tube (close) ;
  • cmd2 continue à lire dans le tube jusqu'à la fin (read renvoie 0), puis se termine (exit).

  • bash détecte la fin des processus, récupère le code de terminaison et supprime les zombies (wait) puis affiche le prompt.

Mécanisme très efficace : bande passante de plusieurs giga-octets par seconde (100 à 1000 fois plus rapide que dans un fichier sur un disque dur).

3.5. Commandes tee et xargs

🍵  La commande tee

La commande tee permet de recopier l'entrée standard à la fois dans un fichier et sur la sortie standard.

Elle est très utile pour enregistrer dans un fichier ce qui est affiché :

$ cmd1 | tee trace.txt

Elle permet aussi d'enregistrer ce qui passe par un tube :

$ cmd1 | tee trace.txt | cmd2

Exemple :

$ find /etc -name "*.conf" 2> /dev/null | tee res.txt | wc -l
797
$ grep fuse res.txt
/etc/fuse.conf

🌵  La commande xargs

La commande xargs permet de transformer la sortie standard en arguments de la commande suivante :

$ cmd1 | xargs cmd2

Si la sortie de cmd1 est la chaîne "a b c d", la commande qui sera exécutée par xargs sera : cmd2 a b c d

Les options les plus courantes de xargs sont :

  • -n X exécute la commande plusieurs fois, par paquet de X arguments
  • -L Y idem par paquet de Y lignes
  • -i cmd2 {} exécute la commande pour chaque ligne, désignée par {}
  • -t affiche les commandes effectuées

Exemple avec -n :

$ seq 10 | xargs echo
1 2 3 4 5 6 7 8 9 10
$ seq 10 | xargs echo toto
toto 1 2 3 4 5 6 7 8 9 10
$ seq 10 | xargs -n 3 echo toto
toto 1 2 3
toto 4 5 6
toto 7 8 9
toto 10

Exemple avec -L :

$ echo -e "a b c\nd e f g\ni j"
a b c
d e f g
i j
$ echo -e "a b c\nd e f g\ni j" | xargs echo -e foo
foo a b c d e f g i j
$ echo -e "a b c\nd e f g\ni j" | xargs -L 1 echo foo
foo a b c
foo d e f g
foo i j
$ echo -e "a b c\nd e f g\ni j" | xargs -L 2 echo foo
foo a b c d e f g
foo i j

Exemple avec des fichiers dont le nom contient un espace :

$ mkdir tmp && cd tmp
$ touch essai.txt "mes idées.txt" notes.txt
$ ls *.txt
essai.txt  'mes idées.txt'  notes.txt
$ ls *.txt | cat        # dans un tube, ls affiche un nom par ligne
essai.txt
mes idées.txt
notes.txt

$ ls *.txt | xargs -t -i cp {} {}.old
cp essai.txt essai.txt.old 
cp 'mes idées.txt' 'mes idées.txt.old'
cp notes.txt notes.txt.old 

$ ls -b     # -b échappe les caractères non graphiques
essai.txt  essai.txt.old  mes\ idées.txt  mes\ idées.txt.old  notes.txt 
notes.txt.old

3.6. Commandes sur le contenu

Dans la suite, les arguments optionnels figurent entre [], comme dans les pages de man. Il ne faut pas recopier les [] en tapant la commande !

🐱  La commande cat

Usage : cat [file]...

Concatène le ou les fichiers ou l'entrée standard et les recopie sur la sortie standard.

$ echo -e "ré\nmi" > f1.txt
$ cat > f2.txt
fa                      # l'utilisateur tape des lignes, lues par cat
sol
^D                      # l'utilisateur tape CTRL D ⟶ fin entrée standard
$ cat f1.txt f2.txt
ré
mi
fa
sol

🚽  La commande wc

Signifie word count.

Usage : wc [-lwc] [file]...

Permet de compter les lignes (-l), les mots (-w) ou les caractères (-c), ou par défaut les trois, dans le ou les fichiers ou sur l'entrée standard.

$ ls *.txt | wc -l
4

🐊  La commande tail

Usage : tail [-n nb] [-f] [file]...

Permet de ne sélectionner que les nb dernières lignes, ou 10 par défaut, du ou des fichiers ou de l'entrée standard.

-f met à jour l'affichage chaque fois que le fichier change.

$ tail -n 20 -f /var/log/syslog

Usage : head [-n nb] [file]...

Permet la même chose sur le début du fichier ou l'entrée standard.

$ find /etc -name "*.conf" | head -5

♻  La commande sort

Usage : sort [-u] [-n] [-r] [file]...

Permet de trier (par ordre alphabétique) les lignes du ou des fichiers ou de l'entrée standard.

Les options principales sont :
-u   les lignes multiples n'apparaissent qu'une fois (unicité)
-n   le tri est fait sur la valeur numérique du premier mot de chaque ligne
-r   ordre du tri inversé
$ du -sk /usr/* | sort -rn | head -5

👫  La commande uniq

Usage : uniq [options] file

Recherche et affiche les lignes adjacentes uniques, lues dans le fichier file ou sur l'entrée standard :

  • sans option, affiche pour chaque groupe identique la première ligne ;
  • avec -u : affiche les lignes adjacentes uniques ;
  • avec -d : n'affiche que les lignes adjacentes multiples, 1 par groupe.
$ echo -e "ga\nbu\nbu\nzo\nga" | uniq
ga
bu
zo
ga
$ echo -e "ga\nbu\nbu\nzo\nga" | uniq -u
ga
zo
ga
$ echo -e "ga\nbu\nbu\nzo\nga" | uniq -d
bu

✂  La commande cut

Usage : cut [-c liste] [-d'C' -f liste] [file]...

Permet de ne récupérer que certains caractères ou champs de chaque ligne, dans le ou les fichiers ou sur l'entrée standard.

  • -c liste : permet de n'afficher que les caractères dont les numéros sont dans la liste.
  • -d'C' -f liste ou -d 'C' -f liste : permet de découper chaque ligne avec comme délimiteur le caractère C et n'afficher que les champs dont les numéros sont dans la liste.
  • liste : sous la forme n1 ou n1,n2... ou n1-n2,...
$ echo "13009" | cut -c 1-2
13
$ echo "ga bu zo meu" | cut -d' ' -f 1,3
ga zo

Les éléments sont toujours affichés dans l'ordre lu et une seule fois :

$ echo "ga bu zo meu" | cut -d' ' -f 2,1,4,1
ga bu meu

💫  La commande tr

Usage : tr [options] set1 [set2]

Permet de transformer un caractère lu sur l'entrée standard en un autre.

  • set1 est une liste de caractères, ou peut être un intervalle par exemple a-z0-9 ;
  • set2 est une autre liste de caractères, de la même taille que set1.

Chaque caractère lu, s'il est dans set1, est remplacé par le caractère correspondant dans set2.

Options :

  • -s : remplace les occurrences multiples de caractères dans set1 par 1 seul.
  • -d : supprime les occurrences de caractères dans set1.
$ echo "J'ai des idées" | tr a-z A-Z
J'AI DES IDéES
$ echo "J'ai des idées" | tr a-z n-za-m     # rot13
J'nv qrf vqérf
$ echo "J'ai des idées" | tr a-z n-za-m | tr a-z n-za-m
J'ai des idées

$ echo -e "salut\n\n\nles\n\namis" | tr -s '\n'    # supprime lignes vides
salut
les
amis

$ echo "voila, je crois, c'est clair !" | tr -d ',;.!'
voila je crois c'est clair
$ echo "voila, je crois, c'est clair !" | tr -d [:punct:]
voila je crois cest clair 

🐶  La commande grep

(Global search for Regular Expressions and Print)

Usage : grep [options] motif [file]...

Affiche les lignes qui possèdent le motif, lues dans chaque fichier ou sur l'entrée standard.

Le motif est une expression régulière dans laquelle

  • ^ désigne le début
  • $ désigne la fin
  • . est un caractère quelconque sauf un retour chariot
  • [...] correspond à 1 caractère parmi ceux de la liste
  • * signifie que le caractère peut être présent 0, 1 ou plusieurs fois
  • | associe des motifs alternatifs : motif1|motif2|...

Si le motif comporte des symboles, il faut le protéger avec des ''.

Principales options :

  • -E : le motif est une expression régulière étendue.
  • -v motif : affiche les lignes qui n'incluent pas le motif.
  • -B n1 -A n2 : affiche n1 lignes avant (before) et n2 lignes après (after).

Exemple :

$ grep -E '^[ ]*PS1' -B 1 -A 1 /etc/* 2> /dev/null