# Commande awk
{{METATOC 4-5}}
La commande **awk** permet d'appliquer un certain nombre d'actions sur un fichier. La syntaxe est inspirée du C
#### syntaxe
```
awk [-Fs] [-v variable] [-f fichier de commandes] 'program' fichier
```
* **-F** Spécifie les séparateurs de champs
* **-v** Définie une variable utilisée à l'intérieur du programme.
* **-f** Les commandes sont lu à partir d'un fichier.
Le **program** est une suite d'action de la forme : motif { action } , le motif permet de determiner sur quels enregistrements est appliquée l'action. Si le motif existe dans l'enregistrement, l'action sera appliquée à la ligne .
Le motif peut etre :\\ * une expression régulière\\ `/expression regulière/`\\ `$0 ~ /expression regulière/`\\ `expression ~ /expression regulière/`\\ `expression !~ /expression regulière/`\\ * une expression BEGIN ou END\\ * une expression de comparaison: `<`, `<=`, `==` , `!=`, `>=`, `>`\\ * une combinaison des trois (à l'aide des opérateurs booléens || ou, && et, ! négation)\\ * une caractérisation des lignes\\ * motif1,motif2 : chaque ligne entre la premiere ligne correspondant au motif1 et la première ligne correspondant au motif2
* Par exemple, pour vérifier certains enregistrements du fichier `/etc/passwd`:
```
awk 'BEGIN { print "Verification du fichier /etc/passwd pour ...";
print "- les utilisateurs avec UID = 0 " ;
print "- les utilisateurs avec UID >= 60000" ;
FS=":"}
$3 == 0 { print "UID 0 ligne "NR" :\n"$0 }
$3 >= 60000 { print "UID >= 60000 ligne "NR" :\n"$0 }
END { print "Fin" }
' /etc/passwd
```
Résultat :
```
Verification du fichier /etc/passwd pour ...
- les utilisateurs avec UID = 0
- les utilisateurs avec UID >= 60000
UID 0 ligne 5 :
root:*:0:b:administrateur:/:/bin/sh
UID >= 60000 ligne 14 :
clown:*:61000:b:utilisateur en erreur:/home/clown:/bin/sh
Fin
```
* Par exemple, pour vérifier le libellé du groupe 20 dans `/etc/group`:
```
awk 'BEGIN { print "Verification du fichier /etc/group";
print "le groupe 20 s'appelle t-il bien users ? " ;
FS=":"}
$1 == "users" && $3 ==20 { print "groupe "$1" a le GID "$3" !" }
END { print "Fin" }
' /etc/group
```
Résultat :
```
Verification du fichier /etc/group
le groupe 20 s'appelle t-il bien users ?
groupe users a le GID 20 !
Fin
```
* Par exemple, pour afficher un interval de lignes avec leur numéro:
```
awk 'NR == 5 , NR == 10 {print NR" : " $0 }' fichier
```
Imprime de la ligne 5 à la ligne 10 , chaque ligne précédée par son numéro
#### La lecture
Les instructions sont des blocs définis par des accolades. Ces blocs d'instructions sont exécutés (selon leurs conditions) durant la lecture du fichier, à chaque enregistrement. De plus, deux blocs spéciaux permettent d'ajouter des instructions à exécuter avant la lecture du premier enregistrement (le bloc **BEGIN**) et après la lecture du dernier enregistrement (le bloc **END**).
Lorsqu'un enregistrement est lu, il est placé dans la variable **$0** (à ne pas confondre avec la variable $0 de Bash et Perl). L'enregistrement est ensuite scindé en plusieurs parties dans un tableau interne, allant de **$1** à **$NF**, en utilisant **FS** comme séparateur de champs (à l'instar d'un split() automatique). En lignes de commandes, la suite de commandes Awk est à placer entre apostrophes. Par exemple :
```
$ awk '{print "ligne",NR,": ",$0," [nombre de champs:",NF,"] [premier champ:",$1,"] [dernier champ:",$NF,"]"}' ./fichier.txt
ligne 1 : ceci est un fichier [nombre de champs: 4 ] [premier champ: ceci ] [dernier champ: fichier ]
ligne 2 : qu'il est bien [nombre de champs: 3 ] [premier champ: qu'il ] [dernier champ: bien ]
```
Une ligne de code suffit pour afficher chaque ligne d'un fichier, le numéro de ligne, le nombre de champs, les séparer et les afficher. Awk peut lire plusieurs fichiers à la suite en les plaçant les uns à la suite des autres, et utiliser un ou plusieurs scripts Awk au lieu de lignes de commandes en utilisant des options -f. Exemple :\\ `awk -f script1.awk -f script2.awk fichier1 fichier2 fichier3`
#### L'affichage
L'une des fonctions les plus utilisées est certainement la fonction print, qui permet l'affichage de texte et variables, en les séparant par une virgule. Les champs sont ensuite séparés par la valeur de la variable OFS (un espace par défaut, voir l'exemple ci-dessus).
La fonction printf(), de syntaxe similaire à celle de C,permet d'afficher les données, de formater la mise en page et l'affichage des nombres. Exemple dans un bloc final (END), pour récupérer le nombre de lignes parcourues :
```
$ awk 'END {printf("| %-20s | %-12s |\n| %-20s | %-12i |\n","fichier",FILENAME,"nombre de lignes",FNR)}' fichier.txt
| fichier | fichier.txt |
| nombre de lignes | 3 |
```
Les variables **%s** et **%i** sont respectivement remplacées par une chaîne de caractères et un entier. En ajoutant %-20s, on place cette valeur dans un espace de 20 caractères en commençant par la gauche (%20s pour la droite). Le restant est rempli par des espaces, ce qui, dans l'exemple ci-dessus, aligne les tubes.
**Les commentaires** sont à placer après un caractère croisillon (**#**).\\ **Une action vide** est représenté par **;**
Les scripts peuvent renvoyer un code retour de sortie par la fonction exit , par défaut égal à 0, et des commandes Shell peuvent être exécutées par la fonction system(), qui retourne le code de sortie de la commande.
```
$ awk 'NR==1 { system("ls -l "FILENAME) } { print $0 }' fichier.txt
-rw-r--r-- 1 dams dams 104 juil. 24 18:55 fichier.txt
bob;adresse;12 rue de la mouette
alice;adresse;3 avenue du goéland
charles;adresse;24 rue de l'albatros
```
L'instruction suivante est identique à print mais en utilisant un format
```
printf format , exp, exp` ou `printf (format,exp , exp )
```
Un format est une chaine de caractères et des constructeurs commencant par %
^ specifieur ^ signification ^
| d | nombre decimal |
| s | chaine de caractères |
| specifieur | signification |
| - | expression justifiée à gauche |
| largeur | largeur d'affichage |
| .precision | longueur maximale d'une chaine de caracteres ou nombre de decimales |
La sortie d'un print ou d'un printf peut être redirigée dans un fichier ou sur un pipe
Les noms de fichiers doivent être entre guillemets sinon ils sont considérés comme des variables
Exemple: le fichier fich.numerote contient le fichier fichier avec les lignes numérotées
```
awk ' { print NR " :" , $0 > "fich.numerote" } ' fichier
```
Exemple: le fichier fich.numerote contient le fichier fichier avec les lignes numérotées sur 3 caractères
```
awk ' { printf "%3d : %s " , NR , $0 > "fich.numerote" } ' fichier |
```
#### Les variables et expressions
##### Les variables prédéfinies
Pour bien utiliser **awk**, il faut connaître les variables de base, au moins **FS**, **NR** et **RS**, utiles dans la plupart des scripts.
^ Variable ^ Signification ^ Valeur par défaut ^
| ARGC | Nombre d'arguments de la ligne de commande | - |
| ARGV | tableau des arguments de la ligne de commnde | - |
| FILENAME | nom du fichier sur lequel on applique les commandes | - |
| FNR | Nombre d'enregistrements du fichier | - |
| FS | separateur de champs en entrée | " " |
| NF | nombre de champs de l'enregistrement courant | - |
| NR | nombre d'enregistrements deja lu | - |
| OFMT | format de sortie des nombres | "%.6g" |
| OFS | separateur de champs pour la sortie | " " |
| ORS | separateur d'enregistrement pour la sortie | "\n" |
| RLENGTH | longueur de la chaine trouvée | - |
| RS | separateur d'enregistrement en entrée | "\n" |
| RSTART | debut de la chaine trouvée | - |
| SUBSEP | separateur de subscript | "\034" |
Les variables personnelles n'ont besoin ni d'être déclarées, ni d'être initialisées dans les blocs principaux. Il n'y a pas non plus de type à déclarer, elles sont auto-typées (attention toutefois aux mélanges entiers/caractères lors des opérations). Les champs du fichier fichier.txt précédent sont séparés par un point-virgule, aussi la variable de séparation de champs, **FS**, doit être modifiée avant la lecture du fichier, dans le bloc **BEGIN**. Les autres blocs d'instructions, qui ne sont pas précédés de **BEGIN**, **END**, ou de conditions, sont exécutés pour chaque enregistrement.
```
$ awk 'BEGIN{FS=";"} {print $3}' fichier.txt
12 rue de la mouette
3 avenue du goéland
24 rue de l'albatros
```
##### Les opérations et affectations arithmétiques
* Les opérateurs arithmétiques sont les opérations usuelles : + - * / % (reste division entière) et ^ (puissance). Tous les calculs sont effectués en virgule flottante.
* La syntaxe de l'affectation : var = expression
* On peut aussi utiliser les operateurs +=, -=, *=, /=, %= et ^= (x+=y équivaut à x=x+y)
##### Les variables de champs
**Rappel** : Les champs de la ligne courant sont : $1, $2, ..., $NF\\ \\ La ligne entière est $0
Ces variables ont les memes propriétés que Les autres variables. Elles peuvent etre reaffectées. Quand $0 est modifiées, les variables $1,$2 ... sont aussi modifiées ainsi que NF.Inversement si une des variables $i est modifiées, $0 est mise à jour.
Les champs peuvent etre specifiés par des expressions, comme $(NF-1) pour l'avant dernier champs.
Par exemple pour créer un nouveau fichier de mot de passe `/etc/passwd.new` en remplacant le shell `/bin/ksh` par `/bin/posix/sh`
```
awk 'BEGIN { FS=":" ;
OFS=":"}
$NF != "/bin/ksh" { print $0 }
$3 == "/bin/ksh" && NF == 7 { $7 = "/bin/posix/sh" ;
print $0 } '
/etc/passwd > /etc/passwd.new
```
#### Les paramètres
Les paramètres sont indiqués par leurs noms suivis d'un caractère = et de leurs valeurs, avant les noms des fichiers. Ils sont ensuite récupérés dans le script par leurs noms, sans caractère additionnel, après le bloc **BEGIN** (si défini). Par exemple, le script `test.awk` suivant (l'opérateur tilde est une condition de contenu) :
```
BEGIN{FS=";"; OFS=" : "}
$1 ~ nom {print $1,$3}
```
```
$ awk -f test.awk nom=alice fichier.txt
alice : 3 avenue du goéland
```
#### Les chaînes de caractères
En plus de fonctions de base, Awk dispose également de fonctions dédiées aux traitements des chaînes de caractères (ou string en anglais), facilitant ce genre d'opérations. La liste de ces fonctions est la suivante :
^ Nom des fonctions ^ signification ^
| gsub(exp,sub,str) | Substitue globalement par la chaîne sub chaque expression régulière exp trouvée dans la chaîne 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 chaîne str, ou 0 si non trouvé. |
| length(str) | Retourne la longueur de la chaîne 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 chaîne str, ou 0 si non trouvé. Affecte les valeurs aux variables RSTART et RLENGTH (cf. 2.4 Les variables). |
| split(str,tab,sep) | Sépare la chaîne str en éléments dans un tableau tab et en utilisant le séparateur sep. Si sep n'est pas renseigné, FS est utilisée par défaut. |
| sprintf("format",exp) | Retourne une chaîne 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 commenç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 chaîne str et retourne la nouvelle chaîne. |
| toupper(str) | Met en majuscules toute la chaîne str et retourne la nouvelle chaîne. |
La concaténation de chaîne de caractères s'effectue sans espace ni caractère spécifique.
Exemple pour numéroter les lignes du fichier
```
awk '{ print NR " : " $0 }' fichier
```
Exemple pour afficher les actions executées lors du passage à l'état 2
```
awk 'BEGIN { FS=":" ;
OFS=":" ;
print " Run Level 2 : Liste des actions "}
$2 ~ /2/ { print "Keyword <<"$3">>, \n Tache <<"$4">>" }
$2 == "" { print "Keyword <<"$3">>, \n Tache <<"$4">>" }
' /etc/inittab > /etc/passwd.new
```
#### 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()|
#### Les fonctions personnelles
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