But de ce tutorial
Nous allons aborder la notion de filtre : un programme qui modifie l'entrée qu'il reçoit. En utilisant des infoducs, nous allons construit des associations de filtres permettant de faire de nouvelles opérations.
echo ; more ; less ; uniq ; cut ; fold ; tr ; grep ; wc ; awk. Soit les 10 commandes les plus utiles pour débuter dans les filtres.
SOMMAIRE DU TUTORIAL
3
a) Commande echo et re-directions
b) Afficher le contenu d'un fichier
c) Afficher une partie d'un fichier
d) Autour des commandes : infoduc, expressions régulières
e) Principaux filtres : uniq, cut, fold, tr
f) Rechercher une séquence dans des fichiers
g) Compter
h) Introduction à awk
La commande echo affiche
par défaut sur la sortie standard (l'écran) ce qui la suit.
Par exemple, echo
Bonjour ! affichera Bonjour !
. On peut effectuer des redirections
sur les commandes ; ainsi, on peut modifier l'entrée (ce que vont recevoir
les commandes) et la sortie (leur résultat). A la base, il y a 3 canaux
utilisés par les commandes : 0 pour l'entrée (stdin en langage
C), 1 pour la sortie classique (stdout en langage C), 2 pour la sortie d'erreur
(stderr en langage C).
Pour effectuer une redirection, on place le caractère
après la commande. Voici les redirections importantes :
> fichier, qui va créer un fichier
et y stocker le résultat de la sortie standard. Si le fichier n'existe
pas, il est créée. S'il existe, il est écrasé
(c'est-à-dire remplacé par ce qu'on y fait à présent).
>> fichier, qui rajoute à
un fichier le résultat de la sortie standard. Si le fichier n'existe
pas, il est créée. S'il existe, ce qu'on y fait à présent
est rajouté (on dit "concaténé").
n> fichier, qui utilise l'opérateur
> avec un canal particulier (1 ou 2).
n>> fichier, qui utilise l'opérateur
>> avec un canal particulier (1
ou 2).
< fichier, utilise le fichier comme
entrée standard. Il doit exister.
Voici pour bien comprendre un ensemble de commandes qu'on tape,
et des commentaires précédés de # :
echo Youhou !
# affiche Youhou !
echo "Youhou !"
# affiche un problème. En effet, la syntaxe est incorrecte, il ne faut
pas de " "
echo Youhou ! > you.txt
# on a maintenant un fichier you.txt contenant Youhou
echo Yeah... > you.txt
# notre fichier you.txt contient maintenant Yeah... car il a été
écrasé
echo "Hmm..." 2> /dev/null
# 2> fait une redirection de la sortie d'erreur. /dev/null est une sorte
de poubelle où on jette les messages inutiles
echo Fin 1>> you.txt
# strictement équivalent à faire >> you.txt dans ce cas.
On rajoute Fin au fichier you.txt (redirige sortie standard).
On peut utiliser quelques caractères spéciaux à l'intérieur
du texte, comme \n qui passe à
la ligne, ou \r qui revient au début
de la ligne. Voici donc le texte que nous voulons :
"Monsieur,
Un monsieur du nom de Van Huik est entré ce matin.
Il a acheté un Van chez un ami à nous. Ce van est gris."
Ce texte est stupide mais il nous servira pour l'occurence des mots (nombre
de fois qu'on trouve un mot). Ainsi, pour le mettre dans un fichier nommé
texte.txt, rien de plus simple :
echo Monsieur,\nUn
monsieur du nom de Van Huik est entré ce matin.\nIl
a acheté un Van chez un ami à nous. Ce van est gris. > texte.txt
D'autres essais peuvent être amusants. Ainsi, essayez echo Mon beau sapin est lourd\rMon gros. On a aussi d'autres possibilités pour la commande echo, comme : \b (reculer d'un caractère), \xxx (afficher le caractère de code ASCII en octal xxx, exemple 068), \\ (faire simplement un anti slash).
Enfin, une remarque importante sur les redirections : selon votre shell (ce qui interprète les commandes de la fenêtre du terminal), et la configuration, elles ne réagissent pas de la même manière. En effet, dans zsh, on a un ! pour forcer l'écriture du fichier, et des protections de base. Nous reviendrons sur le sujet ultérieurement.
b) Afficher le contenu d'un fichier >> vers le sommaire
Il existe deux commandes pour cela : more et less. Exemple pour afficher le fichier précédent : more texte.txt ou less texte.txt. Pour afficher la ligne suivante, il suffit d'appuyer sur entrée ; pour la page suivante, on appuie sur espace.
Quelques options possibles pour more :
- more -c Texte.txt
: efface l'écran avant d'afficher la page suivante
- more -s Texte.txt
: regroupe les lignes blanches en une seule
- more +n Texte.txt
: sauter n lignes avant d'afficher le fichier
Pour ce qui est des commandes interactives dans more, elles sont assez pauvres : s permet desauter une ligne, f de sauter un écran ; il y en a quelques autres, guère plus utile...
Comme la plupart des outils rudimentaires, more se passe de vérification et est donc assez robuste dans le sens où il permet d'afficher n'importe quoi. En revanche, less peut refuser d'afficher le contenu d'un fichier .png (une image)... Cependant, less a beaucoup plus d'options pour le piloter, et en particulier il peut utiliser une liste de fichiers. Par exemple, on peut faire less fichier1.txt fichier2.txt fichier3.txt, il va commencer par ouvrir fichier1.txt et nous propose de naviguer entre ces différents fichiers, qui constituent une liste.
Les possibilités pour piloter la commande less
sont assez intéressantes :
- avec d on peut avancer, et b
reculer
- r permet de rafraîchir l'écran
: si le fichier est modifié alors qu'il est visualisé, on actualise
l'affichage
- on peut marquer un passage du texte avec m
suivi d'une lettre ; pour y revenir, on utilise alors '
suivi de la lettre qu'on a utilisé. On peut aussi faire suivre '
de $ (fin du fichier) ou ^ (début du fichier).
- avec /motif on peut rechercher un motif
dans la suite du fichier ( ?motif permet
de rechercher en arrière)
- avec :e on demande à examiner
un nouveau fichier, on spécifie alors son nom. Il est affiché
et ajouté à la liste des fichiers (si au départ un seul
fichier avait été ouvert, une ligne apparait en bas, se terminant
par (file n of m)).
- avec :p on passe au fichier précédent
(previous) et :n au fichier suivant (next)
- :d supprimer le fichier courant de la
liste des fichiers
- = donne des informations sur le fichier
c) Afficher une partie d'un fichier >> vers le sommaire
Avec head (tête) et
tail (queue), on peut afficher le début
et la fin d'un fichier. Les options suivantes sont valables pour les deux
commandes :
- -n x. Affiche les x premières/dernières
lignes. Exemple : head
texte.txt -n 10 (affiche les 10
premières lignes).
- -c x. Afficher les x premiers/derniers
blocs, en spécifiant l'unité par b (512 octets), k (1 Ko), m
(1 Mo). Exemple : tail
texte.txt -c 3b (afficher les derniers
1,5 Ko : 3 blocs de 512 octets).
Soit long.txt un texte dont on veut les 3 premières lignes et les
16 dernières. On éxécute les commandes suivantes afin
d'extraire les lignes qui nous intéresse et de les mettre dans un nouveau
fichier resume.txt :
head long.txt -n 3 > resume.txt ;
va créer un fichier resume.txt dans lequel on mettra les 3 premières
lignes
tail long.txt -n 16 >> resume.txt
; ajouter les 16 dernières lignes à resume.txt
d) Autour des commandes : infoduc, expressions régulières >> vers le sommaire
On désire associer plusieurs commandes afin de réaliser une
action complexe. Prenons un exemple : on désire afficher l'anté-pénutième
ligne d'un fichier (la ligne qui précède l'avant-dernière).
Pour cela, il suffit d'afficher les 3 dernières lignes et de prendre
la première ainsi obtenue :
tail memo02.pdf -n
3 | head -n 1
Ce | se nomme un 'infoduc' (on parle aussi de tube...) : le résultat de la commande de gauche est envoyé à la commande de droite. Autre exemple : on imagine qu'on est sur un serveur où il y a beaucoup de monde, et on aimerait se déplacer dans la liste des utilisateurs connectés. Rien de plus simple : w | less
Il est aussi possible d'éxécuter un ensemble d'actions. Par exemple, demandons dans une seule ligne à écrire l'heure et la date dans un fichier essai.txt : {time;date} > essai.txt. La structure { } permet de lister un ensemble de commande. Avec ; on demande une exécution séquentielle : time sera fait, puis date. On peut demander à ce que les commande s'exécutent en parallèle, avec &.
Lorsqu'on veut désigner un ensemble d'expressions, on peut utiliser ce qui s'appelle une expression régulière. [] permet de désigner un caractère parmis tous ceux énumérés ; exemple : [a-z] (toutes les lettres minuscules de a à z), [aplfb] (les lettres a, p, l, f et b), [a-fP-O] (les minucules de a à f puis les majuscules de P à O), [0-9] (les chiffres).
On peut aussi demander la négation de l'ensemble désigné avec ^. Par exemple, [^a-zA-Z] correspond à "tout sauf une lettre". A ne pas confondre avec ^ qui représente la position en début de ligne, et $ la position en fin de ligne.
e) Principaux filtres : uniq, cut, fold, tr >> vers le sommaire
La commande uniq permet d'exclure des
lignes se ressemblant. Par défaut, elle exclut toutes les lignes successives
identiques sauf une. Un exemple où elle peut se montrer utile : la
commande finger affiche les utilisateurs
connectés en prenant une ligne pour chaque de leurs terminaux. On imagine
ne pas vouloir connaître tous les terminaux d'où se connectent
les utilisateurs, et ainsi purger la liste. On peut le faire de la façon
suivante :
finger | uniq -w
10
Où -w demande de limiter la comparaison
entre les lignes aux 10 premiers caracteres. Autres options utiles :
- -s n : ne pas comparer les n premiers
caractères
- -f : ignorer la casse des caractères
(c'est-à-dire considérer que BoNjOuR est équivalent à
Bonjour)
La commande cut, comme son nom l'indique,
permet de couper son entrée. Avec -c
on spécifie la liste des caractères à afficher. Par exemple
:
more texte.txt | cut -c
2-7,15-38 ; va
afficher du 2ème au 7ème caractère, puis du 15ème
au 38ème
Avec fold on limite la longueur d'une
ligne et on replie le surplus ; il y a trois options : -b
(compter les octets), -w (compter les
colonnes), -s (couper sur les blancs).
Exemple :
finger | fold -w
10
Dont le résultat commencera par :
Login
Name
Tty
Idle Lo
Le filtre tr est plus intéressant
: il permet de remplacer ou de supprimer une partie du texte. Avec -d
on choisit de détruire, par exemple :
finger | tr -d
[a-z] ;
va supprimer toutes les lettres minuscules. [a-z] signifie "de a à
z"
finger | tr -d [a-z][A-Z] ;
va supprimer toutes les lettres. [a-z][A-Z] signifie "de a à z
et de A à Z"
finger | tr -d [a-zA-Z]
;
même chose écrite différement. Encore une expression régulière.
finger | tr -d [:blank:]
; va supprimer tous les espaces (les blancs)
finger | tr [:blank:] '_' ;
va remplacer tous les espaces par des _
finger | tr [:upper:] [:lower:] ;
va remplacer toutes les majuscules (upper) par des minuscules (lower)
finger | tr ' ' '\n' ;
va remplacer tous les espaces par des retour à la ligne
finger | tr [pts] '-' ;
remplace les lettres p, t et s par -
Pour désigner un ensemble de caractères, on dispose de classes
de caractères prédéfinies : [:alnum:] (lettres et chiffres),
[:alpha:] (lettres), [:blank:] (blancs horizontaux), [:cntrl:] (caractères
de contrôl), [:digit:] (chiffres), [:graph:] (caractères imprimables
sauf blancs), [:lower:] (lettres minuscules), [:print:] (caractères
imprimables y compris les blancs), [:punct:] (ponctuation), [:space:] (espaces),
[:upper:] (lettres majuscules), [:xdigit:] (chiffres hexadecimaux).
f) Rechercher une séquence dans des fichiers >> vers le sommaire
La commande grep est très utile
: elle permet de chercher une chaîne de caractères dans un ensemble
de fichiers, et d'afficher les lignes concernées. Exemple :
echo Nous allons rechercher\nune
séquence.\non cherche ... >
essai.txt ;
on se fait un fichier
grep 'cherche' essai.txt ;
va afficher la première ligne et la troisième : elles contiennent
'cherche'
On peut faire cette recherche sur plusieurs fichiers, par exemple : grep 'bonjour' fichier1.txt fichier2.txt. Dans ce cas, si la chaîne a été trouvée on nous indiquera dans quel fichier, comme fichier1.txt:bonjour tout le monde.
On dispose de nombreuses options :
- -A n pour afficher en plus les n lignes
après la ligne qui contient le motif
- -B n pour précéder la
ligne contenant le motif de n lignes
- -h lorsqu'on donne une liste de fichier,
ne pas précéder le résultat du nom de fichier
- -i ignorer la casse
- -L affiche simplement le nom des fichiers
dans lequel rien n'a été trouvé
- -l affiche simplement le nom des fichiers
où la séquence a été trouvée
Exemple :
echo Bonjour tout le monde > fichier1.txt
echo bonjour tout le monde > fichier2.txt
grep -hi 'bonjour' fichier1.txt
fichier2.txt ;
affiche"Bonjour tout le monde" et "bonjour tout le monde"
Comme avec tr, on peut utilise des classes
de caractères prédéfinies ou des expressions. Par exemple
:
echo 123456 > f1.txt
echo 789 > f2.txt
echo 5 > f3.txt
grep '[1-6]' f1.txt f2.txt f3.txt ;
il y a des chiffres entre 1 et 6 dans f1 et dans f3
grep '[7-9]' f1.txt f2.txt f3.txt ;
il y a des chiffres enter 7 et 9 seulement dans f2
grep '[a-z]' f1.txt f2.txt f3.txt
; il n'y a des caractères dans aucun fichier
Une dernière illustration :
echo Bonjour à tous\nBienvenue
sur ce site\net à bientôt
> bienvenue.txt
echo 7h. Dîner\nMerci d'etre
a l'heure\n12hRepas\nFroid
ou chaud > menu.txt
echo Bonus entre 1h et 3 > miam.txt
grep '^[^1-9]' bienvenue.txt menu.txt miam.txt ;
n'affichera pas menu.txt : il commence par un chiffre
grep '^[A-Z]' bienvenue.txt menu.txt miam.txt ;
n'affichera pas menu.txt pour la même raison
grep '^[A-Z]?' bienvenue.txt menu.txt miam.txt
; n'affichera rien. '?' demande
du même type que [A-Z]...
grep '^[A-Z].' bienvenue.txt menu.txt miam.txt
; n'affichera pas menu.txt. Il ne commence par pas une lettre
g) Compter >> vers le sommaire
La commande wc compte le nombre de lignes, mots ou caractères sur son entrée standard. Avec -l on demande le nombre de lignes, avec -w le nombre de mots, et avec -c le nombre de caractères. On peut par exemple demander combien de lignes contient un texte : wc -l texte.txt
Cependant, cette commande est plus souvent utilisée dans un infoduc.
On peut arriver à s'en servir pour compter le nombre d'occurences d'un
mot dans un texte. Le principe est d'utiliser la commande grep
et de compter le nombre de lignes retenues, mais avec un traitement avant.
En effet, en utilisant la commande grep
directement, si le mot apparait plusieurs fois nous n'obtenons pourtant qu'une
ligne (ce qui compte donc pour 1 seulement).
D'où : more
texte.txt | tr [:space:] '\n' | grep -i
'mot' | wc -l
On envoit le texte à tr qui remplace
tous les espaces par un retour à la ligne. Ainsi chaque mot est sur
une ligne, et si grep trouve une fois
le mot alors il sélectionnera une ligne complète. Au final,
on compte le nombre de lignes.
Evidemment, on peut faire ceci en se passant de wc
et en utilisant l'option -c qui permet
à grep de compter !
Une utilisation plus simple peut-être pour compter le nombre de fichiers dans un répertoire. On sait que ls -1 affiche uniquement un fichier par ligne ; on compte les lignes : ls -1 | wc -l.
h) Introduction à awk >> vers le sommaire
Plus qu'un filtre, awk est un véritable petit langage. Nous allons commencer par l'utiliser comme filtre, comme grep. awk structure ce qu'il reçoit sous forme de mots : $0 représente l'intrégralité de la phrase, $1 le premier mot, $2 le second mot, etc. On considère comme mot les chaînes de caractéres séparées par des espaces.
Un programme awk est une succession de sélecteurs et de traitement. Les sélecteurs sont des expressions décrivant les lignes que l'on recherche ; les traitements sont structurés d'une façon proche du C où on peut utiliser des variables, boucles, tests...
Commencons par voir le découpage en mots, un exemple :
echo Bo n jou r > test.txt ;
on se donne un petit fichier dont on voit clairement le découpage
more test.txt | awk
'{print $0}'
; On sélectionne l'ensemble de la phrase avec $0, et on l'affiche :
print
more test.txt | awk
'{print $1}' ;
On affiche ici le premier mot, qui est 'Bo'
more test.txt | awk
'{print $2}' ;
On affiche le second mot qui est 'n'
more test.txt | awk
'{print $389}' ;
Il n'y a pas de 389ème mot, on affiche donc... du vide.
On va maintenant s'attacher aux sélecteurs. On peut utiliser des expressions
régulières avec le principe suivant : l'expression est entre
'/ /', le * est un caractère spécialisé (i.e. signification
particulière). Imaginons un fichier fictif id.txt, faisons quelques
essais :
cat hip.txt | awk
'/a/' ;
Rappel : cat affiche le contenu du fichier. Ici, on filtre les lignes qui
contiennent un a.
cat hip.txt | awk
'/a.*a/' ; On veut une ligne
qui ait 2 a. Le '.' représente un caractère quelconque, et le
'*' signifie
"un nombre quelconque de fois ce qui précède" (peut-être
nul)
cat hip.txt | awk
'/^a/' ;
On veut une ligne qui commence par un a
cat hip.txt | awk
'/a$/' ;
On veut une ligne qui se finisse par un a
cat hip.txt | awk
'/^\*a$/' ; On veut une ligne qui commence
par un * et finisse par un a. Attention, * est spécialisé
: pour le considérer comme un simple caractère, on le fait précéder
de \
cat hip.txt | awk
'/^\*.*\*$/' ; On veut que la ligne commence et se finisse par
une *
cat hip.txt | awk
'/\$\$*$/' ; On veut une ligne qui ne contienne
que des $
En utilisant la fonction printf, on peut créer
rapidement des variables pour son affichage. Par exemple, si on veut afficher
des mots en particulier, on va faire des variables chaînes de caractère
%s (pour String) :
ls -l | awk
'{printf("Premier mot : %s
\n Troisieme mot : %s \n",$1,$3)}'
Le modèle est printf("message
%s %s %s",$1,$2,$3)
où ce qui est entre " " sera affiché, et ce qui suit
sert de variable à appeler lors de l'affichage. Chaque % est donc associé
à une variable. On peut maintenant définir des chiffres selon
la même idée :
ls -l | awk
'{printf("La taille imaginaire de %s
est %.3f \n",$9,$5/100)}'
%.3f signale qu'on a un nombre dont on affiche la précision jusqu'à 3 chiffres après la virgule. Notre nombre correspond à $5/100 c'est-à-dire le 5ème champ de ls -l (la taille du fichier) divisé par 100. awk arrive sans problème à faire cette opération. Si ce qu'on lui donne n'est pas un nombre, par exemple $1 (le champ des permissions), il ne va pas s'arrêter brutalement mais afficher simplement 0,000 à chaque fois.
Petit bilan d'exemple :
ls -l | awk
'/c/ {printf("%s
contient bien un c et mesure 10 fois %.1f\n",$9,$5/10)}'
Ceci affiche les fichiers dont le nom contient un c, accompagné
de "et mesure 10 fois" avec la taille divisée par 10.