Le langage de programmation awk

Etiquettes: 

Principe

 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.

Etiquettes: 

Syntaxe

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.

Etiquettes: 

Variables spéciales

Variables prédéfinies au lancement de awk

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.

Variables initialisées lors du traitement d'une ligne

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.

Exemples simples

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
$

Etiquettes: 

Critères de sélection

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.

Expressions réguliè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
$

Tests logiques

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
$

Etiquettes: 

Structure d'un script awk

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
$

Etiquettes: 

Opérateurs

Liste des opérateurs disponible dans awk.

Opérateurs arithmétiques

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érateurs de tests

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érateurs logiques

Opérateur Arité Signification
! Binaire Négation
&& Binaire ET logique
|| Binaire OU logique

Divers

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

 

Etiquettes: 

La fonction printf

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
$

Etiquettes: 

Redirections

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
$

Etiquettes: 

Lecture de la ligne suivante : next

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
$

Etiquettes: 

Structures de controle

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.

1 - if

La partie else est facultative.

Syntaxe

if (condition) {
     instruction
     ...
}
else {
     instruction
     ...
}

2 - for

Première syntaxe

for (initialisation ; condition ; incrementation) {
     instruction
     ...
}

Deuxième syntaxe

for (cle in tableau) {
     print cle , tableau[cle]
}

3 - while

Syntaxe

while (condition) {
     instruction
     ...
}

4 - do-while

Syntaxe

do {
     instruction
     ...
} while (condition)

5 - break

Le mot clé break permet d'interrompre une boucle.

Principe

while (1) {
     if (condition) break ;
     instruction ;
}

6 - continue

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 ;
}

Etiquettes: 

Terminer un script

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
}

Etiquettes: 

Tableaux

Dans le langage awk, il existe 2 types de tableaux

Etiquettes: 

Tableaux indicés par un entier

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
$

Etiquettes: 

Tableaux associatifs

Initialiser un tableau

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)
$

Tester l'existence d'un élément

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 ) {
     ...
     ...
}

Supprimer un élément

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.

Etiquettes: 

Les arguments de la ligne de commande

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
$

Etiquettes: 

Fonctions intégrées

Etiquettes: 

Les fonctions sur les chaines de caractères

Les chaines de caractères

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
$

Etiquettes: 

Les fonctions mathématiques

Les fonctions mathématiques

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
$

Etiquettes: 

Autres fonctions

Etiquettes: 

La fonction getline

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
$

Etiquettes: 

La fonction close

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
$

Etiquettes: 

La fonction system

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
$

Etiquettes: 

Fonctions utilisateur

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
$

Etiquettes: 

Exemples de scripts

Etiquettes: 

Analyse des logs postfix

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 :

  1. codeErrorSmtp
  2. mail.awk

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
$

Etiquettes: 

Afficher les infos du processeur et de la mémoire dans une page web

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

infoproc.awk

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&eacute;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
HTML icon infoproc.html5.96 Ko
Etiquettes: