Extrait de Wikipédia :
Awk est le plus souvent utilisé pour la production de fichiers plats aux spécifications particulières (échanges entre différents systèmes d'informations hétérogènes). Il est aussi utilisé comme "parser" de fichiers XML ou de fichiers textes pour générer des commandes SQL à partir des données extraites. Il peut être utilisé aussi pour des opérations de calculs complexes et mise en forme de données brutes pour faire des tableaux statistiques.
On distingue awk, la commande originale, du new awk (nawk), arrivée un peu plus tard sur le marché. Les implémentations GNU de awk, sont en fait des new awk. On trouve en général la commande awk dans /usr/bin sous Unix. Certains systèmes GNU/Linux le mettent dans /bin. En général, elle est dans la variable d'environnement PATH. Cependant, on peut faire des scripts en awk et le shebang (#!/usr/bin/awk -f) devient faux. Le script est donc inutilisable si le binaire n’est pas là où on l’attend.
Il agit comme un filtre programmable prenant une série de lignes en entrée (sous forme de fichiers ou directement via l'entrée standard) et écrivant sur la sortie standard, qui peut être redirigée vers un autre fichier ou programme. Un programme Awk est composé de trois blocs distincts utilisables ou non pour le traitement d'un fichier (prétraitement, traitement, posttraitement). Awk lit sur l'entrée ligne par ligne, puis sélectionne (ou non) les lignes à traiter par des expressions rationnelles (et éventuellement des numéros de lignes). Une fois la ligne sélectionnée, elle est découpée en champs selon un séparateur d'entrée indiqué dans le programme awk par le symbole FS (qui par défaut correspond au caractère espace ou tabulation). Puis les différents champs sont disponibles dans des variables : $1 (premier champ), $2 (deuxième champ), $3 (troisième champ), ..., $NF (dernier champ).
« awk » est aussi l'extension de nom de fichier utilisée pour les scripts écrits dans ce langage.
awk [-F] '{action-awk}' [ fichier1 fichier2 ..... fichiern ]
awk [-F] -f script-awk [ fichier1 fichier2 ..... fichiern ]
La commande awk prend en argument la liste des fichiers à traiter. En l'absence de noms de fichiers sur la ligne de commande, awk travaille sur les données arrivant sur son entrée standard. Cette commande peut donc être placée derrière un tube de communication.
L'option "-F" permet d'initialiser, si besoin, la variable "FS" (Field Separator) correspondant au caractère séparateur de champ.
Le tableau suivant présente les principales variables internes du langage awk présentes en mémoire dès le lancement de la commande. La valeur de ces variables peut éventuellement être modifiée en fonction de la structure des données à traiter.
Nom de la variable | Valeur par défaut | Rôle de la variable |
---|---|---|
RS | Newline (\n) | Record Separator : Caractère séparateur d'enregistrement (lignes). |
FS | Suite d'espaces et/ou de tabulations | Field Separator : Caractères séparateurs de champs. |
OFS | Espace | Output Field Separator : Séparateur de champ utilisé pour l'affichage. |
ORS | Newline (\n) | Output Record Separator : Caractère séparateur d'enregistrement en sortie. |
ARGV | - | Tableau initialisé avec les arguments de la ligne de commande (options et nom du script awk exclus). |
ARGC | - | Nombre d'éléments contenus dans le tableau ARGV. |
ENVIRON | Variables d'environnement exportées par le shell. | Tableau contenant les variables d'environnement exportées par le shell. |
CONVFMT | %.6g | Format de conversion des nombres en String. |
OFMT | %.6g | Format de sortie des nombres. |
SUBSEP | \034 | Caractère de séparation pour les routines internes des tableaux. |
Par défaut, un enregistrement correspond donc à une ligne (suite de caractères terminée par "\n").
Lorsque la variable FS est initialisée avec un minimum de 2 caractères, cette valeur est interprétée comme une expression régulière.
Les enregistrements sont traités successivement. L'enregistrement courant est automatiquement découpé en champs et un certain nombre de variables internes awk sont initialisées. Le tableau suivant donne la liste des principales variables.
Nom de la variable | Valeur de la variable |
---|---|
$0 | Valeur de l'enregistrement courant |
NF | Number of Field : Nombre de champs de l'enregistrement courant. |
$1, $2, ... $NF | $1 contient la valeur du 1er champ, $2 la valeur du 2ème champ etc etc ... et $NF la valeur du dernier champ (NF est remplacé par sa valeur). |
NR | Number : Indice de l'enregistrement courant (NR vaut 1 quand la 1ère ligne est en cours de traitement, puis s'incrémente dès que awk change d'enregistrement). |
FNR | File Number : Indice de l'enregistrement courant relatif au fichier en cours de traitement. |
FILENAME | Nom du fichier en cours de traitement. |
RLENGTH | Longueur du string trouvé par la fonction match() |
RSTART | Première position du string trouvé par la fonction match() |
Contrairement aux variables du shell, le symbole "$" des variables awk $1, $2 etc etc... fait partie du nom des variables.
Premier exemple :
Ici, awk travaille sur le résultat de la commande ps -ef. La partie en rouge représente l'action que awk doit exécuter sur chaque ligne. Les simples quotes sont indispensables pour empêcher le shell d'interpréter les caractères destinés à la commande awk. Les instructions doivent être placées entre accolades. La fonction intégrée print va afficher à l'écran les champs 1 et 8 de chaque ligne.
$ ps -ef | awk '{print $1,$8}'
UID CMD
root init
...
www-data /usr/sbin/apache2
www-data /usr/sbin/apache2
root vzctl:
root -bash
postfix pickup
root ps
root awk
root /usr/lib/postfix/master
postfix qmgr
$
Deuxième exemple :
La fonction print peut également recevoir des chaines de caractères en argument.
$ ps -ef | awk '{print "User : " , $1, "\tCommande : " , $8}'
User : UID Commande : CMD
User : root Commande : init
User : root Commande : [kthreadd/113]
User : root Commande : [khelper/113]
User : root Commande : [init-logger]
...
User : root Commande : vzctl:
User : root Commande : -bash
User : root Commande : ps
User : root Commande : awk
User : root Commande : /usr/lib/postfix/master
$
\t représente le caractère tabulation.
Troisième exemple :
Modification du séparateur de champ "FS" grâce à l'option "-F"
$ cat /etc/passwd | awk -F : '{print $1,$7}'
root /bin/bash
...
nobody /bin/sh
libuuid /bin/sh
postfix /bin/false
sshd /usr/sbin/nologin
mysql /bin/false
$
Quatrième exemple :
$ cat /etc/passwd | awk -F : '{print "User : " , $1 , "\tShell : " , $7}'
User : root Shell : /bin/bash
User : daemon Shell : /bin/sh
User : bin Shell : /bin/sh
User : sys Shell : /bin/sh
User : sync Shell : /bin/sync
User : games Shell : /bin/sh
User : man Shell : /bin/sh
User : lp Shell : /bin/sh
User : mail Shell : /bin/sh
User : news Shell : /bin/sh
User : uucp Shell : /bin/sh
User : proxy Shell : /bin/sh
User : www-data Shell : /bin/sh
User : backup Shell : /bin/sh
User : list Shell : /bin/sh
User : irc Shell : /bin/sh
User : gnats Shell : /bin/sh
User : nobody Shell : /bin/sh
User : libuuid Shell : /bin/sh
User : postfix Shell : /bin/false
User : sshd Shell : /usr/sbin/nologin
User : mysql Shell : /bin/false
$
Si la fonction print ne reçoit pas d'argument, elle affiche $0.
$ cat /etc/passwd | awk -F : '{print}'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$
Il est possible de sélectionner les enregistrements sur lesquels l'action doit être exécutée.
Syntaxe :
awk [-F] 'critère {action-awk}' [fichier1 fichier2 ... fichiern]
Le critère de sélection peut s'exprimer de différentes manières.
Les enregistrements à traiter peuvent être sélectionnés en utilisant les expressions régulières étendues (ERe).
Attention, pour utiliser les quantificateurs {x,y} d'une ERe, il faut utiliser awk avec l'option --posix
Premier exemple :
Afficher les lignes du fichier /etc/passwd contenant /bin/false
$ awk -F':' '/\/bin\/false/ {print $0}' /etc/passwd
postfix:x:101:104::/var/spool/postfix:/bin/false
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$
Par défaut, le critère est mis en correspondance avec $0.
Il est possible de mettre un champ particulier en correspondance avec une expression régulière. Dans ce cas, il faut utiliser l'opérateur de concordance "~" ou de non-concordance "!~".
Deuxième exemple :
Afficher les lignes du fichier /etc/passwd dont le 6ème champ commence par /usr.
$ awk -F':' '$6 ~ /^\/usr/ {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
$
Et à l'inverse
$ awk -F':' '$6 !~ /^\/usr/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/bin/sh
...
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$
Le critère peut être une expression d'opérateurs et renvoyant la valeur de vérité vrai ou faux.
Opérateurs de tests courants
Opérateur | Signification |
---|---|
< | Inférieur |
> | Supérieur |
<= | Inférieur ou égal |
>= | Supérieur ou égal |
== | Test d'égalité |
!= | Test d'inégalité |
~ | Correspondance avec une expression régulière |
!~ | Non-correspondance avec une expression régulière |
! | Négation |
&& | Et logique |
|| | Ou logique |
(expression) | Regroupement |
Exemple :
Afficher les champs 1 et 7 de la ligne 4 et 8 du fichier /etc/passwd
$ awk -F':' 'NR == 4 || NR == 8 {print $1,"==>",$7}' /etc/passwd
sys ==> /bin/sh
lp ==> /bin/sh
$
Le chiffre 0 et la chaîne vide sont des valeurs fausses. Toute autre valeur est vraie.
Afficher uniquement les lignes impaires du fichier /etc/passwd.
Le résultat de l'opération NR%2 représentant le reste de la division par 2 du numéro de ligne en cours de traitement, cela permet d'afficher uniquement les lignes impaires du fichier car seulement les nombres impairs renvoient un reste de division différent de 0 (donc vrai). Equivalent à NR%2!=0
$ nl /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/bin/sh
3 bin:x:2:2:bin:/bin:/bin/sh
4 sys:x:3:3:sys:/dev:/bin/sh
5 sync:x:4:65534:sync:/bin:/bin/sync
6 games:x:5:60:games:/usr/games:/bin/sh
7 man:x:6:12:man:/var/cache/man:/bin/sh
8 lp:x:7:7:lp:/var/spool/lpd:/bin/sh
9 mail:x:8:8:mail:/var/mail:/bin/sh
10 news:x:9:9:news:/var/spool/news:/bin/sh
11 uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
12 proxy:x:13:13:proxy:/bin:/bin/sh
13 www-data:x:33:33:www-data:/var/www:/bin/sh
14 backup:x:34:34:backup:/var/backups:/bin/sh
15 list:x:38:38:Mailing List Manager:/var/list:/bin/sh
16 irc:x:39:39:ircd:/var/run/ircd:/bin/sh
17 gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
18 nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
19 libuuid:x:100:101::/var/lib/libuuid:/bin/sh
20 postfix:x:101:104::/var/spool/postfix:/bin/false
21 sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
22 mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$
$ awk -F':' 'NR%2 {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
$
A l'inverse, afficher uniquement les lignes paires.
$ awk -F':' 'NR%2==0 {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$
Afficher les champs 1 et 7 de la ligne 4 à 8 du fichier /etc/passwd
$ awk -F':' 'NR == 4 , NR == 8 {print $1,"==>",$7}' /etc/passwd
sys ==> /bin/sh
sync ==> /bin/sync
games ==> /bin/sh
man ==> /bin/sh
lp ==> /bin/sh
$
Lorsqu'il y a un certain nombre d'actions à réaliser sur les données, il est plus confortable d'écrire un script awk. Un script awk peut contenir une section BEGIN, une section END, et 0 à x sections intermédiaires. Toute section est facultative.
BEGIN
La section BEGIN est exécutée avant le traitement du premier enregistrement des données. Elle est utilisée essentiellement pour initialiser le contexte d'exécution.
Sections intermédiaires
Il peut y avoir plusieurs sections intermédiaires qui seront exécutées sur chaque enregistrement.
END
La section END est exécutée après le traitement du dernier enregistrement des données. Elle est utilisée pour exploiter les résultats issus du traitement des données.
Commentaires
Un commentaire commence par le caractère "#" et se termine au caractère "\n" (fin de la ligne).
Variables
Des variables personnelles peuvent être créées. Une variable est définie dès qu'elle est initialisée et n'a pas besoin d'être typée. L'utilisation d'une variable qui n'a jamais été définie a pour valeur 0 dans un contexte numérique et chaine vide dans un contexte de chaine.
Exemple :
$ nl script1.awk
1 # Section BEGIN
2 BEGIN {
3 print "Section BEGIN"
4 nb_0=0
5 nb_1=0
6 nb_2=0
7 nb_3=0
8 nb_4=0
9 nb_5=0
10 nb_6=0
11 nb_7=0
12 nb_8=0
13 nb_9=0
14 }
15 # Section intermediaire
16 # Traitement des departements commancant par 0
17 $2 ~ /^0/ {
18 print "Departement commancant par 0 ==> CP : " , $3 , "DEPT : " , $5
19 nb_0+=1
20 }
21 # Section intermediaire
22 # Traitement des departements commancant par 1
23 $2 ~ /^1/ {
24 print "Departement commancant par 1 ==> CP : " , $3 , "DEPT : " , $5
25 nb_1+=1
26 }
27 # Section intermediaire
28 # Traitement des departements commancant par 2
29 $2 ~ /^2/ {
30 print "Departement commancant par 2 ==> CP : " , $3 , "DEPT : " , $5
31 nb_2+=1
32 }
33 # Section intermediaire
34 # Traitement des departements commancant par 3
35 $2 ~ /^3/ {
36 print "Departement commancant par 3 ==> CP : " , $3 , "DEPT : " , $5
37 nb_3+=1
38 }
39 # Section intermediaire
40 # Traitement des departements commancant par 4
41 $2 ~ /^4/ {
42 print "Departement commancant par 4 ==> CP : " , $3 , "DEPT : " , $5
43 nb_4+=1
44 }
45 # Section intermediaire
46 # Traitement des departements commancant par 5
47 $2 ~ /^5/ {
48 print "Departement commancant par 5 ==> CP : " , $3 , "DEPT : " , $5
49 nb_5+=1
50 }
51 # Section intermediaire
52 # Traitement des departements commancant par 6
53 $2 ~ /^6/ {
54 print "Departement commancant par 6 ==> CP : " , $3 , "DEPT : " , $5
55 nb_6+=1
56 }
57 # Section intermediaire
58 # Traitement des departements commancant par 7
59 $2 ~ /^7/ {
60 print "Departement commancant par 7 ==> CP : " , $3 , "DEPT : " , $5
61 nb_7+=1
62 }
63 # Section intermediaire
64 # Traitement des departements commancant par 8
65 $2 ~ /^8/ {
66 print "Departement commancant par 8 ==> CP : " , $3 , "DEPT : " , $5
67 nb_8+=1
68 }
69 # Section intermediaire
70 # Traitement des departements commancant par 9
71 $2 ~ /^9/ {
72 print "Departement commancant par 9 ==> CP : " , $3 , "DEPT : " , $5
73 nb_9+=1
74 }
75 # Section END
76 END {
77 print "Section END"
78 print "Nombre total de lignes : " , NR
79 print "Nombre de departements commencant par 0 : " , nb_0
80 print "Nombre de departements commencant par 1 : " , nb_1
81 print "Nombre de departements commencant par 2 : " , nb_2
82 print "Nombre de departements commencant par 3 : " , nb_3
83 print "Nombre de departements commencant par 4 : " , nb_4
84 print "Nombre de departements commencant par 5 : " , nb_5
85 print "Nombre de departements commencant par 6 : " , nb_6
86 print "Nombre de departements commencant par 7 : " , nb_7
87 print "Nombre de departements commencant par 8 : " , nb_8
88 print "Nombre de departements commencant par 9 : " , nb_9
89 }
$
Section BEGIN
Initialisation des variables personnelles servant de compteur.
Sections intermédiaires
Exécution des traitements spécifiques en fonction du début du numéro des départements.
Section END
Affichage du nombre total de lignes traitées et du nombre de départements regroupés par dizaine du numéro de départements.
Exécution du script
$ awk -f script1.awk depts2012.txt
Section BEGIN
Departement commancant par 0 ==> CP : 01053 DEPT : AIN
Departement commancant par 0 ==> CP : 02408 DEPT : AISNE
Departement commancant par 0 ==> CP : 03190 DEPT : ALLIER
Departement commancant par 0 ==> CP : 04070 DEPT : ALPES-DE-HAUTE-PROVENCE
Departement commancant par 0 ==> CP : 05061 DEPT : HAUTES-ALPES
Departement commancant par 0 ==> CP : 06088 DEPT : ALPES-MARITIMES
Departement commancant par 0 ==> CP : 07186 DEPT : ARDECHE
Departement commancant par 0 ==> CP : 08105 DEPT : ARDENNES
Departement commancant par 0 ==> CP : 09122 DEPT : ARIEGE
Departement commancant par 1 ==> CP : 10387 DEPT : AUBE
Departement commancant par 1 ==> CP : 11069 DEPT : AUDE
Departement commancant par 1 ==> CP : 12202 DEPT : AVEYRON
Departement commancant par 1 ==> CP : 13055 DEPT : BOUCHES-DU-RHONE
Departement commancant par 1 ==> CP : 14118 DEPT : CALVADOS
Departement commancant par 1 ==> CP : 15014 DEPT : CANTAL
Departement commancant par 1 ==> CP : 16015 DEPT : CHARENTE
Departement commancant par 1 ==> CP : 17300 DEPT : CHARENTE-MARITIME
Departement commancant par 1 ==> CP : 18033 DEPT : CHER
Departement commancant par 1 ==> CP : 19272 DEPT : CORREZE
Departement commancant par 2 ==> CP : 2A004 DEPT : CORSE-DU-SUD
Departement commancant par 2 ==> CP : 2B033 DEPT : HAUTE-CORSE
Departement commancant par 2 ==> CP : 21231 DEPT : COTE-D'OR
Departement commancant par 2 ==> CP : 22278 DEPT : COTES-D'ARMOR
Departement commancant par 2 ==> CP : 23096 DEPT : CREUSE
Departement commancant par 2 ==> CP : 24322 DEPT : DORDOGNE
Departement commancant par 2 ==> CP : 25056 DEPT : DOUBS
Departement commancant par 2 ==> CP : 26362 DEPT : DROME
Departement commancant par 2 ==> CP : 27229 DEPT : EURE
Departement commancant par 2 ==> CP : 28085 DEPT : EURE-ET-LOIR
Departement commancant par 2 ==> CP : 29232 DEPT : FINISTERE
Departement commancant par 3 ==> CP : 30189 DEPT : GARD
Departement commancant par 3 ==> CP : 31555 DEPT : HAUTE-GARONNE
Departement commancant par 3 ==> CP : 32013 DEPT : GERS
Departement commancant par 3 ==> CP : 33063 DEPT : GIRONDE
Departement commancant par 3 ==> CP : 34172 DEPT : HERAULT
Departement commancant par 3 ==> CP : 35238 DEPT : ILLE-ET-VILAINE
Departement commancant par 3 ==> CP : 36044 DEPT : INDRE
Departement commancant par 3 ==> CP : 37261 DEPT : INDRE-ET-LOIRE
Departement commancant par 3 ==> CP : 38185 DEPT : ISERE
Departement commancant par 3 ==> CP : 39300 DEPT : JURA
Departement commancant par 4 ==> CP : 40192 DEPT : LANDES
Departement commancant par 4 ==> CP : 41018 DEPT : LOIR-ET-CHER
Departement commancant par 4 ==> CP : 42218 DEPT : LOIRE
Departement commancant par 4 ==> CP : 43157 DEPT : HAUTE-LOIRE
Departement commancant par 4 ==> CP : 44109 DEPT : LOIRE-ATLANTIQUE
Departement commancant par 4 ==> CP : 45234 DEPT : LOIRET
Departement commancant par 4 ==> CP : 46042 DEPT : LOT
Departement commancant par 4 ==> CP : 47001 DEPT : LOT-ET-GARONNE
Departement commancant par 4 ==> CP : 48095 DEPT : LOZERE
Departement commancant par 4 ==> CP : 49007 DEPT : MAINE-ET-LOIRE
Departement commancant par 5 ==> CP : 50502 DEPT : MANCHE
Departement commancant par 5 ==> CP : 51108 DEPT : MARNE
Departement commancant par 5 ==> CP : 52121 DEPT : HAUTE-MARNE
Departement commancant par 5 ==> CP : 53130 DEPT : MAYENNE
Departement commancant par 5 ==> CP : 54395 DEPT : MEURTHE-ET-MOSELLE
Departement commancant par 5 ==> CP : 55029 DEPT : MEUSE
Departement commancant par 5 ==> CP : 56260 DEPT : MORBIHAN
Departement commancant par 5 ==> CP : 57463 DEPT : MOSELLE
Departement commancant par 5 ==> CP : 58194 DEPT : NIEVRE
Departement commancant par 5 ==> CP : 59350 DEPT : NORD
Departement commancant par 6 ==> CP : 60057 DEPT : OISE
Departement commancant par 6 ==> CP : 61001 DEPT : ORNE
Departement commancant par 6 ==> CP : 62041 DEPT : PAS-DE-CALAIS
Departement commancant par 6 ==> CP : 63113 DEPT : PUY-DE-DOME
Departement commancant par 6 ==> CP : 64445 DEPT : PYRENEES-ATLANTIQUES
Departement commancant par 6 ==> CP : 65440 DEPT : HAUTES-PYRENEES
Departement commancant par 6 ==> CP : 66136 DEPT : PYRENEES-ORIENTALES
Departement commancant par 6 ==> CP : 67482 DEPT : BAS-RHIN
Departement commancant par 6 ==> CP : 68066 DEPT : HAUT-RHIN
Departement commancant par 6 ==> CP : 69123 DEPT : RHONE
Departement commancant par 7 ==> CP : 70550 DEPT : HAUTE-SAONE
Departement commancant par 7 ==> CP : 71270 DEPT : SAONE-ET-LOIRE
Departement commancant par 7 ==> CP : 72181 DEPT : SARTHE
Departement commancant par 7 ==> CP : 73065 DEPT : SAVOIE
Departement commancant par 7 ==> CP : 74010 DEPT : HAUTE-SAVOIE
Departement commancant par 7 ==> CP : 75056 DEPT : PARIS
Departement commancant par 7 ==> CP : 76540 DEPT : SEINE-MARITIME
Departement commancant par 7 ==> CP : 77288 DEPT : SEINE-ET-MARNE
Departement commancant par 7 ==> CP : 78646 DEPT : YVELINES
Departement commancant par 7 ==> CP : 79191 DEPT : DEUX-SEVRES
Departement commancant par 8 ==> CP : 80021 DEPT : SOMME
Departement commancant par 8 ==> CP : 81004 DEPT : TARN
Departement commancant par 8 ==> CP : 82121 DEPT : TARN-ET-GARONNE
Departement commancant par 8 ==> CP : 83137 DEPT : VAR
Departement commancant par 8 ==> CP : 84007 DEPT : VAUCLUSE
Departement commancant par 8 ==> CP : 85191 DEPT : VENDEE
Departement commancant par 8 ==> CP : 86194 DEPT : VIENNE
Departement commancant par 8 ==> CP : 87085 DEPT : HAUTE-VIENNE
Departement commancant par 8 ==> CP : 88160 DEPT : VOSGES
Departement commancant par 8 ==> CP : 89024 DEPT : YONNE
Departement commancant par 9 ==> CP : 90010 DEPT : TERRITOIRE
Departement commancant par 9 ==> CP : 91228 DEPT : ESSONNE
Departement commancant par 9 ==> CP : 92050 DEPT : HAUTS-DE-SEINE
Departement commancant par 9 ==> CP : 93008 DEPT : SEINE-SAINT-DENIS
Departement commancant par 9 ==> CP : 94028 DEPT : VAL-DE-MARNE
Departement commancant par 9 ==> CP : 95500 DEPT : VAL-D'OISE
Departement commancant par 9 ==> CP : 97105 DEPT : GUADELOUPE
Departement commancant par 9 ==> CP : 97209 DEPT : MARTINIQUE
Departement commancant par 9 ==> CP : 97302 DEPT : GUYANE
Departement commancant par 9 ==> CP : 97411 DEPT : LA
Departement commancant par 9 ==> CP : 97608 DEPT : MAYOTTE
Section END
Nombre total de lignes : 102
Nombre de departements commencant par 0 : 9
Nombre de departements commencant par 1 : 10
Nombre de departements commencant par 2 : 11
Nombre de departements commencant par 3 : 10
Nombre de departements commencant par 4 : 10
Nombre de departements commencant par 5 : 10
Nombre de departements commencant par 6 : 10
Nombre de departements commencant par 7 : 10
Nombre de departements commencant par 8 : 10
Nombre de departements commencant par 9 : 11
$
Liste des opérateurs disponible dans awk.
Opérateur | Arité | Signification |
---|---|---|
+ | Binaire | Addition |
- | Binaire | Soustraction |
* | Binaire | Multiplication |
/ | Binaire | Division |
% | Binaire | Modulo |
^ | Binaire | Exponentiation |
++ | Unaire | Incrémentation d'une variable d'une unité |
-- | Unaire | Décrémentation d'une variable d'une unité |
+= | Binaire | a+=b equivalent à a=a+b |
-= | Binaire | a-=b équivalent à a=a-b |
*= | Binaire | a*=b équivalent à a=a*b |
/= | Binaire | a/=b équivalent à a=a/b |
%= | Binaire | a%=b équivalent à a=a%b |
^= | Binaire | a^=b équivalent à a=a^b |
Opérateur | Arité | Signification |
---|---|---|
< | Binaire | Inférieur |
> | Binaire | Supérieur |
<= | Binaire | Inférieur ou égal |
>= | Binaire | Supérieur ou égal |
== | Binaire | Test d'égalité |
!= | Binaire | Test d'inégalité |
~ | Binaire | Correspondance avec une ERe |
!~ | Binaire | Non correspondance avec une ERe |
Opérateur | Arité | Signification |
---|---|---|
! | Binaire | Négation |
&& | Binaire | ET logique |
|| | Binaire | OU logique |
Opérateur | Arité | Signification |
---|---|---|
= | Binaire | Affectation |
e1 ? e2 : e3 | Ternaire | Le résultat est égal à e2 si e1 est vrai, égal à e3 si e1 est faux |
e1 e2 | Binaire | Concaténation de e1 et e2 |
awk propose la fonction intégrée printf similaire à celle du langage C. Elle permet de formater les affichages.
printf ("chaine",field1,field2,field3...fieldx)
chaine représente la chaine affichée à l'écran. Elle peut contenir des formats qui seront substitués par la valeur des expressions citées à la suite. Il doit y avoir autant de formats que d'expressions.
Formats souvent utilisés
Format | Signification |
---|---|
%20s | Affichage d'une chaine (string) sur 20 caractères (cadrage à droite par défaut) |
%-20s | Affichage d'une chaine (string) sur 20 caractères avec cadrage à gauche |
%3d | Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) |
%03d | Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) complété par des 0 à gauche |
%-3d | Affichage d'un entier/décimal sur 3 chiffres (cadrage à gauche) |
%+3d | Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) avec affichage systématique du signe (un nombre négatif est toujours affiché avec son signe) |
%10.2f | Affichage d'un nombre flottant sur 10 chiffres dont 2 décimales. (cadrage à droite) |
%+010.2f | Affichage d'un nombre flottant sur 10 chiffres dont 2 décimales, cadrage à droite, affichage systématique du signe, complétion par des 0 à gauche. |
Exemple :
$ date | awk '{printf "%10s\n%-10s\n%d\n%15d\n%015d\n%-10d\n%+10d\n%10.2f\n%+010.2f\n" , $1 , $1 , $4 , $4 , $4 , $4 , $4 , 5.2 , 5.2}'
mer. # %10s
mer. # %-10s
2012 # %d
2012 # %15d
000000000002012 # %015d
2012 # %-10d
+2012 # %+10d
5.20 # %10.2f
+000005.20 # %+010.2f
$
Il est possible de rediriger les sorties du script vers un fichier ou vers une commande du système.
Syntaxe
instruction > "fichier" | Au premier appel, le fichier est ouvert en mode "écrasement", puis écriture. Les écritures suivantes se font en mode "ajout" |
instruction >> "fichier" | Au premier appel, le fichier est ouvert en mode "ajout", puis écriture. Les écritures suivantes se font également en mode "ajout" |
print[f] "..." | "commande" | Le résultat de la fonction print/printf est transmise sur l'entrée standard de la commande système par l'intermédiaire d'un tube (pipe) |
Exemple 1
Ouverture en mode "écrasement"
$ nl script2.awk
1 BEGIN {
2 fichier = "/root/fichier1.txt"
3 print "Ligne 1" > fichier
4 print "Ligne 2" > fichier
5 print "Ligne 3" > fichier
6 close(fichier)
7 }
$ uptime > /root/fichier1.txt
$ cat /root/fichier1.txt
13:47:18 up 7 days, 5:28, 0 users, load average: 0.00, 0.00, 0.00
$ awk -f script2.awk
$ cat /root/fichier1.txt
Ligne 1
Ligne 2
Ligne 3
$
Exemple 2
Ouverture en mode "ajout"
$ nl script3.awk
1 BEGIN {
2 fichier = "/root/fichier1.txt"
3 print "Ligne 1" >> fichier
4 print "Ligne 2" > fichier
5 print "Ligne 3" > fichier
6 close(fichier)
7 }
$ uptime > /root/fichier1.txt
$ cat /root/fichier1.txt
13:50:40 up 7 days, 5:31, 0 users, load average: 0.00, 0.00, 0.00
$ awk -f script3.awk
$ cat /root/fichier1.txt
13:50:40 up 7 days, 5:31, 0 users, load average: 0.00, 0.00, 0.00
Ligne 1
Ligne 2
Ligne 3
$
Exemple 3
Ecriture dans un tube. Trier les lignes par users croissants.
La commande sort est exécutée une seule fois puis fermée dans la section END.
$ nl /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/bin/sh
3 bin:x:2:2:bin:/bin:/bin/sh
4 sys:x:3:3:sys:/dev:/bin/sh
5 sync:x:4:65534:sync:/bin:/bin/sync
6 games:x:5:60:games:/usr/games:/bin/sh
7 man:x:6:12:man:/var/cache/man:/bin/sh
8 lp:x:7:7:lp:/var/spool/lpd:/bin/sh
9 mail:x:8:8:mail:/var/mail:/bin/sh
10 news:x:9:9:news:/var/spool/news:/bin/sh
11 uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
12 proxy:x:13:13:proxy:/bin:/bin/sh
13 www-data:x:33:33:www-data:/var/www:/bin/sh
14 backup:x:34:34:backup:/var/backups:/bin/sh
15 list:x:38:38:Mailing List Manager:/var/list:/bin/sh
16 irc:x:39:39:ircd:/var/run/ircd:/bin/sh
17 gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
18 nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
19 libuuid:x:100:101::/var/lib/libuuid:/bin/sh
20 postfix:x:101:104::/var/spool/postfix:/bin/false
21 sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
22 mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$ awk -F':' '{print $0 | "sort"} END {close("sort")}' /etc/passwd
backup:x:34:34:backup:/var/backups:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
news:x:9:9:news:/var/spool/news:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
root:x:0:0:root:/root:/bin/bash
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
sys:x:3:3:sys:/dev:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
$
L'instruction next interrompt le traitement de la ligne courante et déclenche la lecture de la ligne suivante.
Exemple
Dans le script suivant, les instruction next permettent d'accélérer l'exécution en évitant le traitement de la ligne courante dans les sections suivantes celle où la ligne a été traitée.
$ nl script4.awk
1 # Section BEGIN
2 BEGIN {
3 nb_0=0
4 nb_1=0
5 nb_2=0
6 nb_3=0
7 nb_4=0
8 nb_5=0
9 nb_6=0
10 nb_7=0
11 nb_8=0
12 nb_9=0
13 }
14 # Section intermediaire
15 # Traitement des departements commancant par 0
16 $2 ~ /^0/ {
17 print "Departement commancant par 0 ==> CP : " , $3 , "DEPT : " , $5
18 nb_0+=1
19 next
20 }
21 # Section intermediaire
22 # Traitement des departements commancant par 1
23 $2 ~ /^1/ {
24 print "Departement commancant par 1 ==> CP : " , $3 , "DEPT : " , $5
25 nb_1+=1
26 next
27 }
28 # Section intermediaire
29 # Traitement des departements commancant par 2
30 $2 ~ /^2/ {
31 print "Departement commancant par 2 ==> CP : " , $3 , "DEPT : " , $5
32 nb_2+=1
33 next
34 }
35 # Section intermediaire
36 # Traitement des departements commancant par 3
37 $2 ~ /^3/ {
38 print "Departement commancant par 3 ==> CP : " , $3 , "DEPT : " , $5
39 nb_3+=1
40 next
41 }
42 # Section intermediaire
43 # Traitement des departements commancant par 4
44 $2 ~ /^4/ {
45 print "Departement commancant par 4 ==> CP : " , $3 , "DEPT : " , $5
46 nb_4+=1
47 next
48 }
49 # Section intermediaire
50 # Traitement des departements commancant par 5
51 $2 ~ /^5/ {
52 print "Departement commancant par 5 ==> CP : " , $3 , "DEPT : " , $5
53 nb_5+=1
54 next
55 }
56 # Section intermediaire
57 # Traitement des departements commancant par 6
58 $2 ~ /^6/ {
59 print "Departement commancant par 6 ==> CP : " , $3 , "DEPT : " , $5
60 nb_6+=1
61 next
62 }
63 # Section intermediaire
64 # Traitement des departements commancant par 7
65 $2 ~ /^7/ {
66 print "Departement commancant par 7 ==> CP : " , $3 , "DEPT : " , $5
67 nb_7+=1
68 next
69 }
70 # Section intermediaire
71 # Traitement des departements commancant par 8
72 $2 ~ /^8/ {
73 print "Departement commancant par 8 ==> CP : " , $3 , "DEPT : " , $5
74 nb_8+=1
75 next
76 }
77 # Section intermediaire
78 # Traitement des departements commancant par 9
79 $2 ~ /^9/ {
80 print "Departement commancant par 9 ==> CP : " , $3 , "DEPT : " , $5
81 nb_9+=1
82 next
83 }
84 # Section END
85 END {
86 print "Nombre total de lignes : " , NR
87 print "Nombre de departements commencant par 0 : " , nb_0
88 print "Nombre de departements commencant par 1 : " , nb_1
89 print "Nombre de departements commencant par 2 : " , nb_2
90 print "Nombre de departements commencant par 3 : " , nb_3
91 print "Nombre de departements commencant par 4 : " , nb_4
92 print "Nombre de departements commencant par 5 : " , nb_5
93 print "Nombre de departements commencant par 6 : " , nb_6
94 print "Nombre de departements commencant par 7 : " , nb_7
95 print "Nombre de departements commencant par 8 : " , nb_8
96 print "Nombre de departements commencant par 9 : " , nb_9
97 }
$
$ awk -f script4.awk depts2012.txt
Departement commancant par 0 ==> CP : 01053 DEPT : AIN
Departement commancant par 0 ==> CP : 02408 DEPT : AISNE
Departement commancant par 0 ==> CP : 03190 DEPT : ALLIER
Departement commancant par 0 ==> CP : 04070 DEPT : ALPES-DE-HAUTE-PROVENCE
Departement commancant par 0 ==> CP : 05061 DEPT : HAUTES-ALPES
Departement commancant par 0 ==> CP : 06088 DEPT : ALPES-MARITIMES
Departement commancant par 0 ==> CP : 07186 DEPT : ARDECHE
Departement commancant par 0 ==> CP : 08105 DEPT : ARDENNES
Departement commancant par 0 ==> CP : 09122 DEPT : ARIEGE
Departement commancant par 1 ==> CP : 10387 DEPT : AUBE
Departement commancant par 1 ==> CP : 11069 DEPT : AUDE
Departement commancant par 1 ==> CP : 12202 DEPT : AVEYRON
Departement commancant par 1 ==> CP : 13055 DEPT : BOUCHES-DU-RHONE
Departement commancant par 1 ==> CP : 14118 DEPT : CALVADOS
Departement commancant par 1 ==> CP : 15014 DEPT : CANTAL
Departement commancant par 1 ==> CP : 16015 DEPT : CHARENTE
Departement commancant par 1 ==> CP : 17300 DEPT : CHARENTE-MARITIME
Departement commancant par 1 ==> CP : 18033 DEPT : CHER
Departement commancant par 1 ==> CP : 19272 DEPT : CORREZE
Departement commancant par 2 ==> CP : 2A004 DEPT : CORSE-DU-SUD
Departement commancant par 2 ==> CP : 2B033 DEPT : HAUTE-CORSE
Departement commancant par 2 ==> CP : 21231 DEPT : COTE-D'OR
Departement commancant par 2 ==> CP : 22278 DEPT : COTES-D'ARMOR
Departement commancant par 2 ==> CP : 23096 DEPT : CREUSE
Departement commancant par 2 ==> CP : 24322 DEPT : DORDOGNE
Departement commancant par 2 ==> CP : 25056 DEPT : DOUBS
Departement commancant par 2 ==> CP : 26362 DEPT : DROME
Departement commancant par 2 ==> CP : 27229 DEPT : EURE
Departement commancant par 2 ==> CP : 28085 DEPT : EURE-ET-LOIR
Departement commancant par 2 ==> CP : 29232 DEPT : FINISTERE
Departement commancant par 3 ==> CP : 30189 DEPT : GARD
Departement commancant par 3 ==> CP : 31555 DEPT : HAUTE-GARONNE
Departement commancant par 3 ==> CP : 32013 DEPT : GERS
Departement commancant par 3 ==> CP : 33063 DEPT : GIRONDE
Departement commancant par 3 ==> CP : 34172 DEPT : HERAULT
Departement commancant par 3 ==> CP : 35238 DEPT : ILLE-ET-VILAINE
Departement commancant par 3 ==> CP : 36044 DEPT : INDRE
Departement commancant par 3 ==> CP : 37261 DEPT : INDRE-ET-LOIRE
Departement commancant par 3 ==> CP : 38185 DEPT : ISERE
Departement commancant par 3 ==> CP : 39300 DEPT : JURA
Departement commancant par 4 ==> CP : 40192 DEPT : LANDES
Departement commancant par 4 ==> CP : 41018 DEPT : LOIR-ET-CHER
Departement commancant par 4 ==> CP : 42218 DEPT : LOIRE
Departement commancant par 4 ==> CP : 43157 DEPT : HAUTE-LOIRE
Departement commancant par 4 ==> CP : 44109 DEPT : LOIRE-ATLANTIQUE
Departement commancant par 4 ==> CP : 45234 DEPT : LOIRET
Departement commancant par 4 ==> CP : 46042 DEPT : LOT
Departement commancant par 4 ==> CP : 47001 DEPT : LOT-ET-GARONNE
Departement commancant par 4 ==> CP : 48095 DEPT : LOZERE
Departement commancant par 4 ==> CP : 49007 DEPT : MAINE-ET-LOIRE
Departement commancant par 5 ==> CP : 50502 DEPT : MANCHE
Departement commancant par 5 ==> CP : 51108 DEPT : MARNE
Departement commancant par 5 ==> CP : 52121 DEPT : HAUTE-MARNE
Departement commancant par 5 ==> CP : 53130 DEPT : MAYENNE
Departement commancant par 5 ==> CP : 54395 DEPT : MEURTHE-ET-MOSELLE
Departement commancant par 5 ==> CP : 55029 DEPT : MEUSE
Departement commancant par 5 ==> CP : 56260 DEPT : MORBIHAN
Departement commancant par 5 ==> CP : 57463 DEPT : MOSELLE
Departement commancant par 5 ==> CP : 58194 DEPT : NIEVRE
Departement commancant par 5 ==> CP : 59350 DEPT : NORD
Departement commancant par 6 ==> CP : 60057 DEPT : OISE
Departement commancant par 6 ==> CP : 61001 DEPT : ORNE
Departement commancant par 6 ==> CP : 62041 DEPT : PAS-DE-CALAIS
Departement commancant par 6 ==> CP : 63113 DEPT : PUY-DE-DOME
Departement commancant par 6 ==> CP : 64445 DEPT : PYRENEES-ATLANTIQUES
Departement commancant par 6 ==> CP : 65440 DEPT : HAUTES-PYRENEES
Departement commancant par 6 ==> CP : 66136 DEPT : PYRENEES-ORIENTALES
Departement commancant par 6 ==> CP : 67482 DEPT : BAS-RHIN
Departement commancant par 6 ==> CP : 68066 DEPT : HAUT-RHIN
Departement commancant par 6 ==> CP : 69123 DEPT : RHONE
Departement commancant par 7 ==> CP : 70550 DEPT : HAUTE-SAONE
Departement commancant par 7 ==> CP : 71270 DEPT : SAONE-ET-LOIRE
Departement commancant par 7 ==> CP : 72181 DEPT : SARTHE
Departement commancant par 7 ==> CP : 73065 DEPT : SAVOIE
Departement commancant par 7 ==> CP : 74010 DEPT : HAUTE-SAVOIE
Departement commancant par 7 ==> CP : 75056 DEPT : PARIS
Departement commancant par 7 ==> CP : 76540 DEPT : SEINE-MARITIME
Departement commancant par 7 ==> CP : 77288 DEPT : SEINE-ET-MARNE
Departement commancant par 7 ==> CP : 78646 DEPT : YVELINES
Departement commancant par 7 ==> CP : 79191 DEPT : DEUX-SEVRES
Departement commancant par 8 ==> CP : 80021 DEPT : SOMME
Departement commancant par 8 ==> CP : 81004 DEPT : TARN
Departement commancant par 8 ==> CP : 82121 DEPT : TARN-ET-GARONNE
Departement commancant par 8 ==> CP : 83137 DEPT : VAR
Departement commancant par 8 ==> CP : 84007 DEPT : VAUCLUSE
Departement commancant par 8 ==> CP : 85191 DEPT : VENDEE
Departement commancant par 8 ==> CP : 86194 DEPT : VIENNE
Departement commancant par 8 ==> CP : 87085 DEPT : HAUTE-VIENNE
Departement commancant par 8 ==> CP : 88160 DEPT : VOSGES
Departement commancant par 8 ==> CP : 89024 DEPT : YONNE
Departement commancant par 9 ==> CP : 90010 DEPT : TERRITOIRE
Departement commancant par 9 ==> CP : 91228 DEPT : ESSONNE
Departement commancant par 9 ==> CP : 92050 DEPT : HAUTS-DE-SEINE
Departement commancant par 9 ==> CP : 93008 DEPT : SEINE-SAINT-DENIS
Departement commancant par 9 ==> CP : 94028 DEPT : VAL-DE-MARNE
Departement commancant par 9 ==> CP : 95500 DEPT : VAL-D'OISE
Departement commancant par 9 ==> CP : 97105 DEPT : GUADELOUPE
Departement commancant par 9 ==> CP : 97209 DEPT : MARTINIQUE
Departement commancant par 9 ==> CP : 97302 DEPT : GUYANE
Departement commancant par 9 ==> CP : 97411 DEPT : LA
Departement commancant par 9 ==> CP : 97608 DEPT : MAYOTTE
Nombre total de lignes : 102
Nombre de departements commencant par 0 : 9
Nombre de departements commencant par 1 : 10
Nombre de departements commencant par 2 : 11
Nombre de departements commencant par 3 : 10
Nombre de departements commencant par 4 : 10
Nombre de departements commencant par 5 : 10
Nombre de departements commencant par 6 : 10
Nombre de departements commencant par 7 : 10
Nombre de departements commencant par 8 : 10
Nombre de departements commencant par 9 : 11
$
Awk propose des structures de controle que l'on retrouve dans la plupart des langages de programmation. La syntaxe est héritée du langage C.
La partie else est facultative.
Syntaxe
if (condition) {
instruction
...
}
else {
instruction
...
}
Première syntaxe
for (initialisation ; condition ; incrementation) {
instruction
...
}
Deuxième syntaxe
for (cle in tableau) {
print cle , tableau[cle]
}
Syntaxe
while (condition) {
instruction
...
}
Syntaxe
do {
instruction
...
} while (condition)
Le mot clé break permet d'interrompre une boucle.
Principe
while (1) {
if (condition) break ;
instruction ;
}
Le mot clé continue permet de remonter immédiatement à la condition, sans exécuter la suite de la boucle.
Principe
while (1) {
if (condition) continue ;
instruction ;
}
L'instruction exit permet de terminer un script à tout moment en retournant un statut au système.
Exemple
{
if ( NF < 3 ) exit 1 ;
print $1, $2, $3
}
END {
exit 0
}
Dans le langage awk, il existe 2 types de tableaux
L'indice de départ est au choix.
Exemple
Traitement du fichier /etc/passwd.
Ligne 2, initialisation de la variable FS.
Ligne 5, le contenu de la variable $1 correspondant au user est stocké dans le tableau user[] indicé à partir de 1.
Ligne 8 à 10, on parcourt le tableau et on affiche le contenu en formatant l'affichage avec printf.
$ nl script5.awk
1 BEGIN {
2 FS=":"
3 }
4 {
5 user[NR]=$1
6 }
7 END {
8 for (indice = 1 ; indice <= NR ; indice++ ) {
9 printf ("User num %2d : %-20s\n" , indice , user[indice]) ;
10 }
11 }
Exécution
$ awk -f script5.awk /etc/passwd
User num 1 : root
User num 2 : daemon
User num 3 : bin
User num 4 : sys
User num 5 : sync
User num 6 : games
User num 7 : man
User num 8 : lp
User num 9 : mail
User num 10 : news
User num 11 : uucp
User num 12 : proxy
User num 13 : www-data
User num 14 : backup
User num 15 : list
User num 16 : irc
User num 17 : gnats
User num 18 : nobody
User num 19 : libuuid
User num 20 : postfix
User num 21 : sshd
User num 22 : mysql
$
Les tableaux associatifs ont leurs éléments indicés par une chaine de caractères. Cet indice alphanumérique est considéré comme la clé et l'élément correspondant est nommé valeur.
Exemple
Le fichier depts2012.txt liste tous les départements par numéro de région.
$ cat depts2012.txt
REGION DEP CHEFLIEU TNCC NCC NCCENR
82 01 01053 5 AIN Ain
22 02 02408 5 AISNE Aisne
83 03 03190 5 ALLIER Allier
93 04 04070 4 ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93 05 05061 4 HAUTES-ALPES Hautes-Alpes
93 06 06088 4 ALPES-MARITIMES Alpes-Maritimes
82 07 07186 5 ARDECHE Ardèche
21 08 08105 4 ARDENNES Ardennes
...
11 95 95500 2 VAL-D'OISE Val-d'Oise
01 971 97105 3 GUADELOUPE Guadeloupe
02 972 97209 3 MARTINIQUE Martinique
03 973 97302 3 GUYANE Guyane
04 974 97411 0 LA REUNION La Réunion
06 976 97608 0 MAYOTTE Mayotte
$
Le script suivant doit compter le nombre de départements qu'il y a par numéro de région.
La section BEGIN est vide car il n'y a pas besoin d'initialiser des variables.
Ligne 3, une condition est renseignée afin de ne pas traiter la première ligne du fichier qui correspond aux en-têtes.
Ligne 4, mise à jour du tableau nbdepts[] ayant comme clé la valeur du premier champ correspondant au numéro de la région et incrémenté de 1 à chaque fois que le champ 1 de la ligne traitée correspond à la clé.
Ligne 7 à 9, on parcourt le tableau et on affiche les résultats.
$ nl script6.awk
1 BEGIN {
2 }
3 NR != 1{
4 nbdepts[$1]+=1
5 }
6 END {
7 for ( region in nbdepts) {
8 printf("Region num : %02d ==> %3d departement(s)\n" , region , nbdepts[region])
9 }
10 }
$
Exécution
$ awk -f script6.awk depts2012.txt
Region num : 01 ==> 1 departement(s)
Region num : 02 ==> 1 departement(s)
Region num : 03 ==> 1 departement(s)
Region num : 04 ==> 1 departement(s)
Region num : 11 ==> 8 departement(s)
Region num : 06 ==> 1 departement(s)
Region num : 21 ==> 4 departement(s)
Region num : 22 ==> 3 departement(s)
Region num : 23 ==> 2 departement(s)
Region num : 31 ==> 2 departement(s)
Region num : 24 ==> 6 departement(s)
Region num : 25 ==> 3 departement(s)
Region num : 26 ==> 4 departement(s)
Region num : 41 ==> 4 departement(s)
Region num : 42 ==> 2 departement(s)
Region num : 43 ==> 4 departement(s)
Region num : 52 ==> 5 departement(s)
Region num : 53 ==> 4 departement(s)
Region num : 54 ==> 4 departement(s)
Region num : 72 ==> 5 departement(s)
Region num : 73 ==> 8 departement(s)
Region num : 74 ==> 3 departement(s)
Region num : 82 ==> 8 departement(s)
Region num : 83 ==> 4 departement(s)
Region num : 91 ==> 5 departement(s)
Region num : 93 ==> 6 departement(s)
Region num : 94 ==> 2 departement(s)
$
Le mot clé in permet de tester l'existence d'une clé dans un tableau associatif. Cette expression retourne vrai si la clé est présente, faux dans le cas contraire.
cle in tableau
Cette expression peut donc être utilisée comme condition d'une structure de contrôle.
if ( cle in tableau ) {
...
...
}
Il est possible de supprimer un élément d'un tableau associatif en utilisant la syntaxe suivante.
delete tableau[cle]
La paire clé-valeur est supprimée.
awk fourni un mécanisme qui permet de passer des arguments à un script au moment de son appel. Les variables ARGC et ARGV sont initialisées par awk et permettent de traiter les valeurs passées sur la ligne de commandes. La syntaxe doit être obligatroirement du genre var=value et placé avant le ou les fichiers à traiter.
Exemple
Avec le fichier depts2012.txt. Ecrire un script awk permettant de rechercher dans le fichier des enregistrements bien précis en lui passant comme argument un code région (champ 1), un code département (champ 2), un code postal (champ3) ou un département (champ5).
$ cat depts2012.txt
REGION DEP CHEFLIEU TNCC NCC NCCENR
82 01 01053 5 AIN Ain
22 02 02408 5 AISNE Aisne
83 03 03190 5 ALLIER Allier
93 04 04070 4 ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93 05 05061 4 HAUTES-ALPES Hautes-Alpes
93 06 06088 4 ALPES-MARITIMES Alpes-Maritimes
82 07 07186 5 ARDECHE Ardèche
21 08 08105 4 ARDENNES Ardennes
...
11 92 92050 4 HAUTS-DE-SEINE Hauts-de-Seine
11 93 93008 3 SEINE-SAINT-DENIS Seine-Saint-Denis
11 94 94028 2 VAL-DE-MARNE Val-de-Marne
11 95 95500 2 VAL-D'OISE Val-d'Oise
01 971 97105 3 GUADELOUPE Guadeloupe
02 972 97209 3 MARTINIQUE Martinique
03 973 97302 3 GUYANE Guyane
04 974 97411 0 LA_REUNION La Réunion
06 976 97608 0 MAYOTTE Mayotte
$
Le script suivant traitera au maximum 4 arguments. La variable $1 devra être strictement égale à la valeur de l'argument coderegion, la variable $2 devra être strictement égale à la valeur de l'argument codedept, la variable $3 devra être strictement égale à la valeur de l'argument codepostal et enfin la variable $5 devra être strictement égale à la valeur de l'argument dept. Tous ces arguments sont bien sûr facultatifs. Les instructions next permettent de ne pas afficher plusieurs fois une même ligne correspondante à plusieurs sections intermédiaires.
$ nl script9.awk
1 BEGIN {
2 print "ARGC = " , ARGC
3 for (i=0 ; i<ARGC ; i++) {
4 printf ("ARGV[%d] = %s\n", i , ARGV[i])
5 }
6 }
7 $1 == coderegion {
8 print $0
9 next
10 }
11 $2 == codedept {
12 print $0
13 next
14 }
15 $3 == codepostal {
16 print $0
17 next
18 }
19 $5 == dept {
20 print $0
21 next
22 }
$
Exécution du script ayant comme argument de recherche coderegion=93
$ awk -f script9.awk coderegion=93 depts2012.txt
ARGC = 3
ARGV[0] = awk
ARGV[1] = coderegion=93
ARGV[2] = depts2012.txt
93 04 04070 4 ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93 05 05061 4 HAUTES-ALPES Hautes-Alpes
93 06 06088 4 ALPES-MARITIMES Alpes-Maritimes
93 13 13055 4 BOUCHES-DU-RHONE Bouches-du-Rhône
93 83 83137 2 VAR Var
93 84 84007 0 VAUCLUSE Vaucluse
$
Exécution du script ayant comme argument de recherche codedept=44
$ awk -f script9.awk codedept=44 depts2012.txt
ARGC = 3
ARGV[0] = awk
ARGV[1] = codedept=44
ARGV[2] = depts2012.txt
52 44 44109 3 LOIRE-ATLANTIQUE Loire-Atlantique
$
Exécution du script ayant comme argument de recherche codepostal=85191
$ awk -f script9.awk codepostal=85191 depts2012.txt
ARGC = 3
ARGV[0] = awk
ARGV[1] = codepostal=85191
ARGV[2] = depts2012.txt
52 85 85191 3 VENDEE Vendée
$
Exécution du script ayant comme argument de recherche dept=VAR
$ awk -f script9.awk dept=VAR depts2012.txt
ARGC = 3
ARGV[0] = awk
ARGV[1] = dept=VAR
ARGV[2] = depts2012.txt
93 83 83137 2 VAR Var
$
Exécution du script avec tous les arguments renseignés
$ awk -f script9.awk coderegion=93 codedept=44 codepostal=75056 dept=HAUTE-SAVOIE depts2012.txt
ARGC = 6
ARGV[0] = awk
ARGV[1] = coderegion=93
ARGV[2] = codedept=44
ARGV[3] = codepostal=75056
ARGV[4] = dept=HAUTE-SAVOIE
ARGV[5] = depts2012.txt
93 04 04070 4 ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93 05 05061 4 HAUTES-ALPES Hautes-Alpes
93 06 06088 4 ALPES-MARITIMES Alpes-Maritimes
93 13 13055 4 BOUCHES-DU-RHONE Bouches-du-Rhône
52 44 44109 3 LOIRE-ATLANTIQUE Loire-Atlantique
82 74 74010 3 HAUTE-SAVOIE Haute-Savoie
11 75 75056 0 PARIS Paris
93 83 83137 2 VAR Var
93 84 84007 0 VAUCLUSE Vaucluse
$
En plus de fonctions de base, awk dispose également de fonctions dédiées aux traitements des chaines de caractères, facilitant ce genre d'opérations. La liste de ces fonctions est la suivante :
Fonction de string | Description |
---|---|
gsub(exp,sub,str) | Substitue globalement par la chaine sub chaque expression régulière exp trouvée dans la chaine str et retourne le nombre de substitutions. Si str n'est pas indiquée, par défaut $0 est utilisé. |
index(str,st) |
Retourne la position du string st dans la chaine str, ou 0 si non trouvé. |
length(str) | Retourne la longueur de la chaine str. Si str n'est pas indiquée, par défaut $0 est utilisé. |
match(str,exp) | Retourne la position de l'expression régulière exp dans la chaine str, ou 0 si non trouvé. Affecte les valeurs aux variables RSTART et RLENGTH. |
split(str,tab,sep) | Sépare la chaine str en éléments dans un tableau tab et en utilisant le séparateur sep. Si sep n'est pas renseigné, FS est utilisé par défaut. |
sprintf("format",exp) | Retourne une chaine au lieu de l'affichage vers la sortie standard, contrairement à printf(). |
sub(exp,sub,str) | Comme gsub(), mais ne substitue par sub que la première expression exp trouvée dans str. |
substr(str,pos,long) | Retourne une partie du string str commançant à la position pos et de longueur long. Si long n'est pas indiqué, substr() utilise tout le reste de str. |
tolower(str) | Met en minuscules toute la chaine str et retourne la nouvelle chaine. |
toupper(str) | Met en majuscules toute la chaine str et retourne la nouvelle chaine. |
Exemple :
gsub : remplacer par un @ toutes les lettres a et A du fichier depts2012.txt
$ head depts2012.txt | awk '{gsub(/a|A/,"@") ; print}'
REGION DEP CHEFLIEU TNCC NCC NCCENR
82 01 01053 5 @IN @in
22 02 02408 5 @ISNE @isne
83 03 03190 5 @LLIER @llier
93 04 04070 4 @LPES-DE-H@UTE-PROVENCE @lpes-de-H@ute-Provence
93 05 05061 4 H@UTES-@LPES H@utes-@lpes
93 06 06088 4 @LPES-M@RITIMES @lpes-M@ritimes
82 07 07186 5 @RDECHE @rdèche
21 08 08105 4 @RDENNES @rdennes
73 09 09122 5 @RIEGE @riège
$
index : connaitre la position d'un caractère dans une chaine
$ head depts2012.txt | awk '{pos=index($5,"-") ; print "Position du tiret : " , pos , "\tdans la chaine : " , $5}'
Position du tiret : 0 dans la chaine : NCC
Position du tiret : 0 dans la chaine : AIN
Position du tiret : 0 dans la chaine : AISNE
Position du tiret : 0 dans la chaine : ALLIER
Position du tiret : 6 dans la chaine : ALPES-DE-HAUTE-PROVENCE
Position du tiret : 7 dans la chaine : HAUTES-ALPES
Position du tiret : 6 dans la chaine : ALPES-MARITIMES
Position du tiret : 0 dans la chaine : ARDECHE
Position du tiret : 0 dans la chaine : ARDENNES
Position du tiret : 0 dans la chaine : ARIEGE
$
length : connaitre le nombre de caractères dans une chaine
$ tail depts2012.txt | awk '{lg=length($5) ; printf ("Il y a %3d caracteres dans la chaine %30s\n" , lg , $5)}'
Il y a 7 caracteres dans la chaine ESSONNE
Il y a 14 caracteres dans la chaine HAUTS-DE-SEINE
Il y a 17 caracteres dans la chaine SEINE-SAINT-DENIS
Il y a 12 caracteres dans la chaine VAL-DE-MARNE
Il y a 10 caracteres dans la chaine VAL-D'OISE
Il y a 10 caracteres dans la chaine GUADELOUPE
Il y a 10 caracteres dans la chaine MARTINIQUE
Il y a 6 caracteres dans la chaine GUYANE
Il y a 10 caracteres dans la chaine LA_REUNION
Il y a 7 caracteres dans la chaine MAYOTTE
$
match : connaitre la position d'une expression dans une chaine
$ tail depts2012.txt | awk '{pos=match($5,/-DE-/) ; printf("Position de l expression recherchee \"-DE-\" : %2d dans la chaine %20s\tRSTART = %2d\tRLENGTH = %2d\n" , pos , $5 , RSTART , RLENGTH)}'
Position de l expression recherchee "-DE-" : 0 dans la chaine ESSONNE RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 6 dans la chaine HAUTS-DE-SEINE RSTART = 6 RLENGTH = 4
Position de l expression recherchee "-DE-" : 0 dans la chaine SEINE-SAINT-DENIS RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 4 dans la chaine VAL-DE-MARNE RSTART = 4 RLENGTH = 4
Position de l expression recherchee "-DE-" : 0 dans la chaine VAL-D'OISE RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 0 dans la chaine GUADELOUPE RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 0 dans la chaine MARTINIQUE RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 0 dans la chaine GUYANE RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 0 dans la chaine LA_REUNION RSTART = 0 RLENGTH = -1
Position de l expression recherchee "-DE-" : 0 dans la chaine MAYOTTE RSTART = 0 RLENGTH = -1
$
split : séparer une chaine en éléments dans un tableau
$ tail depts2012.txt | awk '/-/{split($5,tab,"-") ; printf("tab1 = %10s\ttab2 = %10s\ttab3 = %10s\n" , tab[1] , tab[2] , tab[3])}'
tab1 = HAUTS tab2 = DE tab3 = SEINE
tab1 = SEINE tab2 = SAINT tab3 = DENIS
tab1 = VAL tab2 = DE tab3 = MARNE
tab1 = VAL tab2 = D'OISE tab3 =
$
sprintf (contrairement à la commande printf, avec sprintf le retour chariot \n n'est pas obligatoire)
$ tail depts2012.txt | awk '/-/{split($5,tab,"-") ; chaine=sprintf("tab1 = %10s\ttab2 = %10s\ttab3 = %10s" , tab[1] , tab[2] , tab[3]) ; print chaine}'
tab1 = HAUTS tab2 = DE tab3 = SEINE
tab1 = SEINE tab2 = SAINT tab3 = DENIS
tab1 = VAL tab2 = DE tab3 = MARNE
tab1 = VAL tab2 = D'OISE tab3 =
$
sub : remplacer par un @ la première lettre a ou A de chaque ligne du fichier depts2012.txt
$ head depts2012.txt | awk '{sub(/a|A/,"@") ; print}'
REGION DEP CHEFLIEU TNCC NCC NCCENR
82 01 01053 5 @IN Ain
22 02 02408 5 @ISNE Aisne
83 03 03190 5 @LLIER Allier
93 04 04070 4 @LPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93 05 05061 4 H@UTES-ALPES Hautes-Alpes
93 06 06088 4 @LPES-MARITIMES Alpes-Maritimes
82 07 07186 5 @RDECHE Ardèche
21 08 08105 4 @RDENNES Ardennes
73 09 09122 5 @RIEGE Ariège
$
substr : extraire une partie d'une chaine
$ tail depts2012.txt | awk '{chaine=substr($5,1,3) ; printf("Les 3 premieres lettres de %20s sont : %3s\n" , $5 , chaine)}'
Les 3 premieres lettres de ESSONNE sont : ESS
Les 3 premieres lettres de HAUTS-DE-SEINE sont : HAU
Les 3 premieres lettres de SEINE-SAINT-DENIS sont : SEI
Les 3 premieres lettres de VAL-DE-MARNE sont : VAL
Les 3 premieres lettres de VAL-D'OISE sont : VAL
Les 3 premieres lettres de GUADELOUPE sont : GUA
Les 3 premieres lettres de MARTINIQUE sont : MAR
Les 3 premieres lettres de GUYANE sont : GUY
Les 3 premieres lettres de LA_REUNION sont : LA_
Les 3 premieres lettres de MAYOTTE sont : MAY
$
tolower : convertir une chaine majuscule en minuscule
$ tail depts2012.txt | awk '{chaine=tolower($5) ; printf("%20s en minuscule : %20s\n" , $5 , chaine)}'
ESSONNE en minuscule : essonne
HAUTS-DE-SEINE en minuscule : hauts-de-seine
SEINE-SAINT-DENIS en minuscule : seine-saint-denis
VAL-DE-MARNE en minuscule : val-de-marne
VAL-D'OISE en minuscule : val-d'oise
GUADELOUPE en minuscule : guadeloupe
MARTINIQUE en minuscule : martinique
GUYANE en minuscule : guyane
LA_REUNION en minuscule : la_reunion
MAYOTTE en minuscule : mayotte
$
toupper : convertir une chaine minuscule en majuscule
$ tail depts2012.txt | awk '{chaine=toupper($6) ; printf("%20s en MAJUSCULE : %20s\n" , $6 , chaine)}'
Essonne en MAJUSCULE : ESSONNE
Hauts-de-Seine en MAJUSCULE : HAUTS-DE-SEINE
Seine-Saint-Denis en MAJUSCULE : SEINE-SAINT-DENIS
Val-de-Marne en MAJUSCULE : VAL-DE-MARNE
Val-d'Oise en MAJUSCULE : VAL-D'OISE
Guadeloupe en MAJUSCULE : GUADELOUPE
Martinique en MAJUSCULE : MARTINIQUE
Guyane en MAJUSCULE : GUYANE
Mayotte en MAJUSCULE : MAYOTTE
$
awk dispose également de fonctions dédiées aux traitements numériques. Celles-ci sont les suivantes :
Fonction mathématique | Description |
---|---|
cos(r) | Cosinus de l'angle r (r en radians) |
exp(x) | Exponentiel de x |
int(x) | Valeur entière de x |
log(x) | Logarithme de x |
sin(r) | Sinus de l'angle r (r en radians) |
sqrt(x) | Racine carrée de x |
atan2(y,x) | Arc tangente de y/x |
rand() | Nombre pseudo-aléatoire compris entre 0 et 1 |
srand(n) | Réinitialise la fonction rand() |
Exemple :
cos
$ echo 50 | awk '{print cos($1)}'
0.964966
$
exp
$ echo 5 | awk '{print exp($1)}'
148.413
$
int
$ echo 5.4 | awk '{print $1 , " ==> " , int($1)}'
5.4 ==> 5
$
log
$ echo 5 | awk '{print log($1)}'
1.60944
$
sin
$ echo 50 | awk '{print sin($1)}'
-0.262375
$
sqrt
$ echo 81 | awk '{print sqrt($1)}'
9
$
atan2
$ echo 50 25 | awk '{print atan2($1,$2)}'
1.10715
$
rand
$ echo | awk '{print rand()}'
0.795735
$ echo | awk '{print rand()}'
0.321886
$ echo | awk '{print int(rand()*1000)}'
792
$
La fonction getline permet de lire la ligne suivante du flux sans remonter au début du traitement (contrairement à next) et de lire une ligne à partir d'un fichier, de l'entrée standard ou d'un tube.
Valeur de retour :
- 1 en cas de succès
- 0 en fin de fichier
- -1 en cas d'erreur
Il ne faut pas mettre de parenthèse lors de l'appel de la fonction getline.
Syntaxe
getline [variable]
Lecture de la ligne suivante du flux.
getline [variable] < "fichier"
Lecture d'une ligne à partir d'un fichier
"commande" | getline [variable]
Lecture d'une ligne provenant du résultat d'une commande du système.
La ligne lue par getline est stockée dans la variable variable ou $0 si aucun nom de variable n'est spécifié. Le nom de fichier "-" représente l'entrée standard.
Premier exemple :
Reconstituer des phrases écrites sur plusieurs lignes et délimitées par un caractère spécifique.
Le fichier suivant contient des phrases scindées en plusieurs lignes. Le scindement est caractérisé par un anti-slash en fin de ligne.
$ cat text.txt
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \
Maecenas porttitor congue massa. \
Fusce posuere, magna sed pulvinar ultricies,purus lectus malesuada libero, \
sit amet commodo magna eros quis urna.
Nunc viverra imperdiet enim. \
Fusce est. \
Vivamus a tellus.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. \
Proin pharetra nonummy pede. Mauris et orci.
Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.
Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. \
Mauris eget neque at sem venenatis eleifend. \
Ut nonummy.
$.
Le script suivant va analyser chaque ligne du fichier et reconstituer l'intégralité des phrases grâce à la fonction getline.
$ nl script10.awk
1 {
2 ligne=$0
3 # Tant que la ligne se termine par \
4 while(ligne ~ /\\$/){
5 # Suppression de \
6 sub(/\\$/,"",ligne)
7 # Lecture de la ligne suivante
8 getline nextline
9 # Concatenation de ligne et nextline
10 ligne=ligne nextline
11 }
12 # Affichage de la ligne globale
13 printf("------- Contenu de la phrase %d -------\n%s\n",++num,ligne)
14 }
$
Exécution du script
$ awk -f script10.awk text.txt
------- Contenu de la phrase 1 -------
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies,purus lectus malesuada libero, sit amet commodo magna eros quis urna.
------- Contenu de la phrase 2 -------
Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.
------- Contenu de la phrase 3 -------
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.
------- Contenu de la phrase 4 -------
Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.
------- Contenu de la phrase 5 -------
Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.
$
Deuxième exemple :
Lire une entrée clavier et le contenu d'un fichier extérieur au flux courant.
Rechercher le nom d'un département dans le fichier depts2012.txt en saisissant son nom au clavier.
$ cat depts2012.txt
REGION DEP CHEFLIEU TNCC NCC NCCENR
82 01 01053 5 AIN Ain
22 02 02408 5 AISNE Aisne
83 03 03190 5 ALLIER Allier
93 04 04070 4 ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93 05 05061 4 HAUTES-ALPES Hautes-Alpes
...
01 971 97105 3 GUADELOUPE Guadeloupe
02 972 97209 3 MARTINIQUE Martinique
03 973 97302 3 GUYANE Guyane
04 974 97411 0 LA_REUNION La Réunion
06 976 97608 0 MAYOTTE Mayotte
$
Le script suivant demande une saisie au clavier et recherche dans le fichier précédent le nom saisi.
$ nl script11.awk
1 BEGIN{
2 # Boucle infinie
3 while(1){
4 printf("Rechercher le nom (ctrl+d pour quitter): ")
5 # Lecture clavier sur l'entree standard "-"
6 # Arret du programme si le code retour de getline est different de 1
7 if((getline nom < "-") != 1) break
8 # Si aucun nom saisi, on recommence
9 if(length(nom)==0) continue
10 trouve="false"
11 # Boucle de lecture du fichier depts2012.txt
12 # Tant que le code retour de getline est egal a 1
13 while((getline < "depts2012.txt")==1){
14 # Comparaison faite sans tenir compte de la casse
15 if(tolower($5)==tolower(nom)){
16 trouve="true"
17 # Affichage du resultat trouve
18 print $0
19 # On sort de la boucle while
20 break
21 }
22 }
23 # Fermeture du fichier
24 close("depts2012.txt")
25 # Affichage du message si le nom n'a pas ete trouve
26 if(trouve=="false") print nom , "n'est pas dans le fichier"
27 }
28 # Affichage du message avant l'arret du programme
29 print "\nA bientot"
30 }
$
Exécution du script
$ awk -f script11.awk
Rechercher le nom (ctrl+d pour quitter): ain
82 01 01053 5 AIN Ain
Rechercher le nom (ctrl+d pour quitter): somme
22 80 80021 3 SOMME Somme
Rechercher le nom (ctrl+d pour quitter):
Rechercher le nom (ctrl+d pour quitter): var
93 83 83137 2 VAR Var
Rechercher le nom (ctrl+d pour quitter):
A bientot
$
Troisième exemple :
Utiliser getline pour lire le résulat d'une commande système.
$ nl script12.awk
1 BEGIN{
2 "date" | getline
3 print "$0 = ",$0
4 for(i=1;i<=NF;i++){
5 printf("$%d = %s\n",i,$i)
6 }
7 }
$ awk -f script12.awk
$0 = Tue May 15 19:35:25 CEST 2012
$1 = Tue
$2 = May
$3 = 15
$4 = 19:35:25
$5 = CEST
$6 = 2012
$
La fonction close permet de fermer un fichier ou un tube de communication. Si l'appel à cette fonction est omis, les ressources sont libérées à la terminaison du script.
L'ordre de fermeture est intéressant dans les cas suivants :
- pouvoir se repositionner en début de fichier au sein du même processus (fermeture puis réouverture)
- fermer un tube de communication pour s'en servir à nouveau
- libérer les ressources au fur et à mesure des besoins (le système limite les processus, en ce qui concerne le nombre de fichiers/tubes ouverts simultanément).
Syntaxe
close("fichier")
close("commande")
Il est indispensable de fermer un fichier pour pouvoir le relire à partir du début.
Exemple :
$ nl script13.awk
1 BEGIN {
2 fichier = "/root/fichier1.txt"
3 i=1
4 while(i<=10){
5 print rand() > fichier
6 i++
7 }
8 # Fermeture du fichier
9 close(fichier)
10 while((getline<fichier)==1){
11 print $0
12 }
13 }
$ awk -f script13.awk
0.692303
0.532958
0.423364
0.484409
0.455168
0.0133607
0.554077
0.375875
0.325945
0.164046
$
La fonction system permet d'exécuter une commande du système.
Syntaxe
system("commande")
La fonction retourne le statut renvoyé par la commande.
Exemple :
$ awk 'BEGIN{system("uptime")}'
21:06:15 up 5 days, 2:22, 0 users, load average: 0.00, 0.00, 0.00
$
Idem mais en se servant d'une variable
$ awk 'BEGIN{fic="depts2012.txt";system("ls -l " fic)}'
-rw-r--r-- 1 root root 3504 May 10 14:05 depts2012.txt
$
Les fonctions personnelles peuvent être définies dans le script awk, en dehors des blocs d'instructions, ou dans un autre script qui sera également appelé par l'option -f <script>.
Une fonction se définit par le mot clé function suivi du nom de la fonction et de ses paramètres. Une fonction peut recevoir de 0 à n arguments et retourner une valeur explicite.
Syntaxe
function nom_fonction (param1, param2, ..., paramn) {
instructions
return valeur
}
Tous les paramètres de la fonction sont des variables locales. Toute autre variable définie dans la fonction est globale.
Appel d'une fonction
valeur=nom_fonction(val1, val2, ..., valn)
Il ne doit pas y avoir d'espace entre le nom de la fonction et la parenthèse ouvrante.
Exemple
$ nl script14.awk
1 function modulo(nb){
2 mod=nb%2
3 if(mod==0){
4 chaine=sprintf("%d est un nombre pair" , nb)
5 }
6 else{
7 chaine=sprintf("%d est un nombre impair" , nb)
8 }
9 return chaine
10 }
11 {
12 for(i=1;i<=NF;i++){
13 print modulo($i)
14 }
15 }
$ echo "20 21 23 56 43 2.4 rr" | awk -f script14.awk
20 est un nombre pair
21 est un nombre impair
23 est un nombre impair
56 est un nombre pair
43 est un nombre impair
2 est un nombre impair
0 est un nombre pair
$
Le script suivant analyse les logs postfix et retourne le nombre de messages par code erreur SMTP.
3 niveaux de visualisation sont proposés dont le jour et le mois en cours.
Pour fonctionner, le script à besoin d'un fichier additionnel répertoriant la liste des codes erreurs et leurs descriptions.
Téléchargement des fichiers :
Détail du fichier codeErrorSmtp :
$ cat codeErrorSmtp
X.1.0|Other address status
X.1.1|Bad destination mailbox address
X.1.2|Bad destination system address
X.1.3|Bad destination mailbox address syntax
X.1.4|Destination mailbox address ambiguous
X.1.5|Destination mailbox address valid
X.1.6|Mailbox has moved
X.1.7|Bad sender's mailbox address syntax
X.1.8|Bad sender's system address
X.2.0|Other or undefined mailbox status
X.2.1|Mailbox disabled, not accepting messages
X.2.2|Mailbox full
X.2.3|Message length exceeds administrative limit.
X.2.4|Mailing list expansion problem
X.3.0|Other or undefined mail system status
X.3.1|Mail system full
X.3.2|System not accepting network messages
X.3.3|System not capable of selected features
X.3.4|Message too big for system
X.4.0|Other or undefined network or routing status
X.4.1|No answer from host
X.4.2|Bad connection
X.4.3|Routing server failure
X.4.4|Unable to route
X.4.5|Network congestion
X.4.6|Routing loop detected
X.4.7|Delivery time expired
X.5.0|Other or undefined protocol status
X.5.1|Invalid command
X.5.2|Syntax error
X.5.3|Too many recipients
X.5.4|Invalid command arguments
X.5.5|Wrong protocol version
X.6.0|Other or undefined media error
X.6.1|Media not supported
X.6.2|Conversion required and prohibited
X.6.3|Conversion required but not supported
X.6.4|Conversion with loss performed
X.6.5|Conversion failed
X.7.0|Other or undefined security status
X.7.1|Delivery not authorized, message refused
X.7.2|Mailing list expansion prohibited
X.7.3|Security conversion required but not possible
X.7.4|Security features not supported
X.7.5|Cryptographic failure
X.7.6|Cryptographic algorithm not supported
X.7.7|Message integrity failure
$
Détail du script commenté :
$ nl mail.awk
1 BEGIN{
2 # Si le nombre d'arguments est egal a 1
3 # Arret du programme
4 # Il faut au moins 2 arguments (1 nom de fichier de log)
5 # Le script peut accepter plusieurs fichiers de log
6 if(ARGC==1){
7 print "Nombre d'arguments incorrect"
8 print "Syntaxe : awk -f mail.awk fichierLogMail1 [fichierLogMailn...]"
9 exit 1
10 }
11 # Initialisation de la variable contenant le nom du fichier
12 # des codes erreurs SMTP
13 errorSmtp="codeErrorSmtp"
14 # Execution d'une commande systeme pour tester si le fichier existe
15 # Arret du programme si le fichier n'existe pas
16 if((system("test -f " errorSmtp))==1){
17 print "Fichier codeErrorSmtp manquant"
18 exit 1
19 }
20 # Sauvegarde des donnees du fichier codeErrorSmtp
21 # dans un tableau associatif avec le code en cle
22 while((getline < errorSmtp) == 1){
23 ligne=$0
24 split(ligne,tab,"|")
25 tabErr[tab[1]]=tab[2]
26 }
27 # Fermeture du fichier codeErrorSmtp
28 close(errorSmtp)
29 # Affichage du menu
30 while(1){
31 # Si le choix est passe en argument
32 if(ARGV[1] ~ /^choix=/){
33 split(ARGV[1],tab,"=")
34 choix=tab[2]
35 if(choix>=1 && choix<=3) break
36 }
37 print "Choix date"
38 print " 1 - all"
39 print " 2 - day"
40 print " 3 - month"
41 printf("Choix : ")
42 # Lecture de l'entree clavier
43 # Si erreur, on quitte la boucle while
44 if((getline choix < "-")!=1) break
45 # Si aucune saisie, reaffichage du menu
46 if(length(choix)==0) continue
47 # Si le choix est compris entre 1 et 3 on quitte la boucle while
48 if(choix>=1 && choix<=3) break
49 }
50 deb=systime()
51 # Si le choix est egal a rien on quitte le programme
52 if(choix==""){
53 print "Bye"
54 exit 2
55 }
56 print "-------------------------------------------"
57 print "------- Analyse des fichiers de log -------"
58 print "-------------------------------------------"
59 }
60 # Lecture des fichiers de log
61 # Si la ligne courante contient l'expression recherchee
62 $0 ~ /[0-9][0-9][0-9] [0-9]\.[0-9]\.[0-9]/ && (choix==1 || (choix==2 && $1==getMonth() && $2==getDay()) || (choix==3 && $1==getMonth())) {
63 # Recherche de la position du premier caractere de l'expression xxx x.x.x
64 pos=match($0,/[0-9][0-9][0-9] [0-9]\.[0-9]\.[0-9]/)
65 # Extraction de l'expression xxx x.x.x
66 code=substr($0,pos,9)
67 # Sauvegarde de l'expression comme cle du tableau associatif
68 # et incrementation du compteur
69 codeSmtp[code]+=1
70 }
71 END{
72 # Parcourt du tableau associatif codeSmtp
73 totalMessages=0
74 for(i in codeSmtp){
75 # Affichage des lignes du tableau
76 # et de la description du code avec la fonction descCode
77 printf("Code %s (%-45s) ==> %5d message(s)\n",i,descCode(i),codeSmtp[i])
78 totalMessages+=codeSmtp[i]
79 }
80 # Fin du programme
81 fin=systime()
82 duree=fin-deb
83 printf("Total : %d messages\n%d lignes traitees en %d sec\n",totalMessages,NR,duree)
84 exit 0
85 }
86 # Fonction permettant la recherche de la description du code
87 function descCode(code){
88 # Extraction des 4 derniers caracteres du code à partir du 6eme caractere
89 codex=substr(code,6,4)
90 # Concatenation de la lettre "X" aux 4 derniers caracteres du code
91 codex="X" codex
92 # Extraction de la description en fonction du code
93 desc=tabErr[codex]
94 # Si le code erreur n'existe pas alors OK
95 desc=(desc != "" ? desc : "OK")
96 # Retour de la description au programme appelant
97 return desc
98 }
99 # Fonction permettant d'obtenir le numero du jour en cours
100 function getDay(){
101 day=int(strftime("%d"))
102 return day
103 }
104 # Fonction permettant d'obtenir le nom du mois en cours
105 function getMonth(){
106 month=strftime("%b")
107 return month
108 }
$
Exécution du script :
$ awk -f mail.awk /var/log/mail.log
Choix date
1 - all
2 - day
3 - month
Choix : 3
-------------------------------------------
------- Analyse des fichiers de log -------
-------------------------------------------
Code 554 5.7.1 (Delivery not authorized, message refused ) ==> 1992 message(s)
Code 550 5.1.1 (Bad destination mailbox address ) ==> 23 message(s)
Code 250 2.0.0 (OK ) ==> 1620 message(s)
Total : 3635 messages
42719 lignes traitees en 0 sec
$
En passant le choix de la date en argument :
$ awk -f mail.awk choix=3 /var/log/mail.log
-------------------------------------------
------- Analyse des fichiers de log -------
-------------------------------------------
Code 554 5.7.1 (Delivery not authorized, message refused ) ==> 1992 message(s)
Code 550 5.1.1 (Bad destination mailbox address ) ==> 23 message(s)
Code 250 2.0.0 (OK ) ==> 1620 message(s)
Total : 3635 messages
42719 lignes traitees en 0 sec
$
Exemple extrait du Linux/Magazine N° 131 d'octobre 2010 page 52.
Le script infoproc.awk utilise les fichiers virtuels /proc/cpuinfo et /proc/meminfo pour extraire les informations à afficher dans une page HTML infoproc.html. Les fichiers sont à placer en derniers paramètres et peuvent être traités différemment par les variables FILENAME et FNR. La sortie du script est redirigée vers le fichier infoproc.html.
L'en-tête et la fin de page HTML sont codés dans le bloc BEGIN et END.
Le fichier virtuel /proc/cpuinfo contient des enregistrements séparés par une ligne vide. Chaque champs des enregistrements est défini sur une ligne. Pour ce fichier, la variable de séparation d'enregistrement RS est donc égale à un saut de ligne "\n\n" et la variable de séparation des champs FS est égale à un retour-chariot "\n". Le fichier /proc/meminfo contient un seul enregistrement et les champs sont séparés par un retour-chariot, aussi les variables RS et FS n'ont pas à être modifiées entre ces fichiers.
Télécharger le script
Détail du script infoproc.awk
$ nl infoproc.awk
1 BEGIN{
2 RS="\n\n" # Separateur d'enregistrement
3 FS="\n" # Separateur de ligne
4 # En-tete et debut de page HTML
5 print "<html>"
6 print "<head>"
7 print "<title>Informations CPU et mémoire</title>"
8 print "<style type=\"text/css\">"
9 print "table {border: solid thin; padding 10px; margin 5px}"
10 print "table.proc {color: DarkSlateBlue; border-color: DarkSlateBlue}"
11 print "table.proc caption {color: white; background: DarkSlateBlue; text-align: center}"
12 print "table.mem {color: DarkGreen; border-color: DarkGreen}"
13 print "table.mem caption {color: white; background: DarkGreen; text-align: center}"
14 print "</style>"
15 print "</head>"
16 print "<body>"
17 print "<table><tr>"
18 }
19 FILENAME ~ /cpuinfo$/ { print "<td valign=\"top\"><table class=\"proc\">"}
20 FILENAME ~ /meminfo$/ { print "<td valign=\"top\"><table class=\"mem\">"}
21 {
22 for(i=1; i<=NF; i++){
23 split($i, cpu, ":")
24 if(i==1) print "<caption>", cpu[1], cpu[2], "</caption>"
25 else print "<tr><td>", cpu[1], "</td><td>", cpu[2], "</td></tr>"
26 }
27 print "</table></td>"
28 }
29 END{
30 # Fin de page HTML
31 print "</tr></table>"
32 print "</body>"
33 print "</html>"
34 }
$
Exécution du script
$ awk -f /root/infoproc.awk /proc/cpuinfo /proc/meminfo > /var/www/infoproc.html
Résultat obtenu
Fichier attaché | Taille |
---|---|
infoproc.html | 5.96 Ko |