| Next revision | Previous revision |
| informatique:linux:awk [2010/04/28 14:50] – créée pteu | informatique:linux:awk [2025/02/17 14:14] (current) – [Exemples] case-insensitive search pteu |
|---|
| {{tag>langage programmation}} | |
| |
| |
| ======awk====== | ======awk====== |
| |
| C'est une commande (un langage) de manipulation de chaînes de caractères d'un fichier ou d'un flux en entrée. | C'est un langage de manipulation de chaînes de caractères d'un fichier ou d'un flux en entrée. |
| |
| Les différentes commandes que l'on peut passer sont de la forme **motif { action }**. | Les différentes commandes que l'on peut passer sont de la forme **motif { action }**. |
| ''awk'' découpe le flux de caractère par ligne (enregistrement) et colonne (champ) ; on peut récupérer les colonnes avec $1, $2,... ,$NF (dernière colonne) et $0 qui correspond à la ligne complète. Les champs peuvent etre specifiés par des expressions, comme $(NF-1) pour l'avant dernier champs. | **awk** traite séquentiellement le flux de caractères (fichier en paramètre ou flux en entrée) par ligne (enregistrement) et colonne (champ) ; on peut récupérer les colonnes avec les variables $1, $2, .., $10 ,$NF (dernière colonne) et $0 qui correspond à la ligne complète. Les champs peuvent être spécifiés par des expressions, comme $(NF-1) pour l'avant dernier champs. |
| |
| ====Le motif==== | ====Le motif==== |
| |
| * une expression régulière : "/regexp/" ; "expression ~ /regexp/" ou sa négation "expression !~ /regexp/" | * une expression régulière : "/regexp/" ; "expression ~ /regexp/" ou sa négation "expression !~ /regexp/" |
| * BEGIN ou END | Pour utiliser une variable bash comme regexp avec awk, on peut procéder ainsi : |
| | <code> |
| | awk '$1 ~ /^'$VAR'/ {print}' |
| | </code> |
| | * BEGIN ou END (BEGIN permet de faire des actions avant le traitement du fichier ; END permet de les faire à la fin du traitement) |
| * une comparaison (<, >, =>, !=, ...) | * une comparaison (<, >, =>, !=, ...) |
| * une combianison des 3 séparés d'un opérateur booléen (|| ou &&) | * une combinaison des 3 séparés d'un opérateur booléen (|| ou &&) |
| |
| ====Les options==== | ====Les options==== |
| |
| * ''-f <fichier>'' la commande s'applique à un fichier | * ''-f <fichier>'' précise un fichier de script awk |
| | <code bash> |
| | vi test.awk |
| | BEGIN{FS=";"; OFS=" : "} |
| | $0 ~ "test" {print $0} |
| | :x |
| | |
| | awk -f test.awk fichier.txt |
| | ceci est un test |
| | </code> |
| * ''-F "<séparateur>"'' spécifie le séparateur de champs (" " (espace) par défaut). NB : on peut spécifier plusieurs séparateurs en les séparant par "|" : | * ''-F "<séparateur>"'' spécifie le séparateur de champs (" " (espace) par défaut). NB : on peut spécifier plusieurs séparateurs en les séparant par "|" : |
| <code> | <code bash> |
| echo "toto;tata-titi" | awk -F":|-" '{print $1" "$2" "$3}' | echo "toto;tata-titi" | awk -F":|-" '{print $1" "$2" "$3}' |
| toto tata titi | toto tata titi |
| | </code> |
| | * ''-v var="val"'' : permet d'affecter une valeur //val// à une variable //var// avant la partie BEGIN |
| | <code bash> |
| | HOST=toto ; echo titi | awk -v h=$HOST '{print "h="h}' |
| | h=toto |
| | </code> |
| | |
| | Autre façon de passer des variables dans un fichier de script : |
| | <code bash> |
| | vi test.awk |
| | BEGIN{FS=";"; OFS=" : "} |
| | $0 ~ rechch {print $0} |
| | :x |
| | |
| | awk -f test.awk rechch=test fichier.txt |
| | ceci est un test |
| </code> | </code> |
| |
| ====Les variables prédéfinies==== | ====Les variables prédéfinies==== |
| |
| ''<variable>'' <desc> <valeur par défaut> | ^ Variable ^ Description ^ Valeur par défaut ^ |
| * ''ARGC'' Nombre d'arguments de la ligne de commande | | ARGC | nombre d'arguments de la ligne de commande || |
| * ''ARGV'' tableau des arguments de la ligne de commnde | | ARGV | tableau des arguments de la ligne de commande || |
| * ''FILENAME'' nom du fichier sur lequel on applique les commandes | | FILENAME | nom du fichier sur lequel on applique les commandes || |
| * ''FNR'' Nombre d'enregistrements du fichier | | ENVIRON | tableau associatif des variables d'environnement || |
| * ''FS'' separateur de champs en entrée | | FNR | nombre d'enregistrements parcourus du fichier courant || |
| * ''NF'' nombre de champs de l'enregistrement courant | | FS | séparateur de champs (en entrée) | <tab>, <espace> ou <retour chariot> contigus | |
| * ''NR'' nombre d'enregistrements deja lu | | NF | nombre de champs de l'enregistrement courant || |
| * ''OFMT'' format de sortie des nombres | | NR | nombre d'enregistrements parcouru (tous fichiers confondus) || |
| * ''OFS'' separateur de champs pour la sortie | | OFMT | format de sortie des nombres || |
| * ''ORS'' separateur d'enregistrement pour la sortie | | OFS | séparateur de champs pour la sortie | <espace> | |
| * ''RLENGTH'' longueur de la chaine trouvée | | ORS | séparateur d'enregistrement pour la sortie | <retour chariot> | |
| * ''RS'' separateur d'enregistrement en entrée | | RLENGTH | longueur de la chaine trouvée || |
| * ''RSTART'' debut de la chaine trouvée | | RS | séparateur d'enregistrement en entrée | <retour chariot> | |
| * ''SUBSEP'' separateur de subscript | | RSTART | début de la chaine trouvée || |
| | | SUBSEP | séparateur de subscript || |
| |
| ====Les fonctions==== | ====Les fonctions texte==== |
| |
| Les paramètres sont soit des chaines de caractère (s et t), soit des regexp (r) soit des entiers (i et n). | Les paramètres sont soit des chaines de caractères (s et t), soit des regexp (r) soit des entiers (i et n). |
| * ''gsub(r,s,t)'' : sur la chaine t, remplace toutes les occurance de r par s | * ''gsub(r,s,t)'' : sur la chaine t, remplace toutes les occurrence de r par s |
| * ''index(s,t)'' : retourne la position la plus à gauche de la chaine t dans la chaine s | * ''index(s,t)'' : retourne la position la plus à gauche de la chaine t dans la chaine s |
| * ''length(s)'' : retourne la longueur de la chaine s | * ''length(s)'' : retourne la longueur de la chaine s |
| * ''match(s,r)'' : retourne l'index ou s correspond à r et positionne RSTART et RLENTH | * ''match(s,r)'' : retourne l'index ou s correspond à r et positionne RSTART et RLENTH |
| * ''split(s,a,fs)'' : split s dans le tableau a sur fs, retourne le nombre de champs | * ''split(s,a,fs)'' : split s dans le tableau a sur fs, retourne le nombre de champs |
| * ''sprintf(fmt,liste expressions)'' : retourne la liste des expressions formatée suivant fmt | * ''sprintf(fmt,liste expressions)'' : retourne la liste des expressions formatée suivant fmt |
| * ''sub(r,s,t)'' : comme gsub, mais remplace uniquement la première occurrence | * ''sub(r,s,t)'' : comme gsub, mais remplace uniquement la première occurrence |
| * ''substr(s,i,n)'' : retourne la sous chaine de s commençant en i et de taille n | * ''substr(s,i,n)'' : retourne la sous chaine de s commençant en i et de taille n |
| | * ''tolower(s)'' : passer la chaîne en minuscules |
| | * ''toupper(s)'' : passer la chaîne en majuscules |
| | * ''count[s]'' : compte le nombre d’occurrence de s |
| | |
| | ===Le cas printf=== |
| | |
| | **printf** et ses dérivées sont des fonctions d'affichage de texte qui permettent de formater la sortie (pour un résultat bien léché comme on aime). Voici quelques cas d'utilisation commentés : |
| | <code bash> |
| | # afficher les logins et description des utilisateurs locaux qui utilisent le shell /bin/bash |
| | # en alignant les logins à droite et sur 20 caractères |
| | awk -F: '$7 ~ /\/bin\/bash/ {printf "%-20s %s\n", $1, $5}' /etc/passwd |
| | root root |
| | dude a dude |
| | robert le gros robert |
| | |
| | # puisque l'affichage est par colonne, on va afficher les noms pour être plus parlant |
| | # on utilise l'instruction BEGIN qui, comme on l'a vu, ne s'exécute qu'une fois en début de script |
| | awk -F: 'BEGIN {printf "%-20s %s\n", "login:","description:"} $7 ~ /\/bin\/bash/ {printf "%-20s %s\n", $1, $5}' /etc/passwd |
| | login: description: |
| | root root |
| | dude a dude |
| | robert le gros robert |
| | |
| | # on peut même définir un format d'affichage pour ne pas avoir a le retaper à chaque printf : |
| | awk -F: 'BEGIN {format = "%-20s %s\n"; printf format, "login:","description:"; printf format, "---","---"} $7 ~ /\/bin\/bash/ {printf format, $1, $5}' /etc/passwd |
| | login: description: |
| | --- --- |
| | root root |
| | dude a dude |
| | robert le gros robert |
| | |
| | # la même commande, en plus lisible : |
| | awk -F: 'BEGIN {format = "%-10s %s\n" |
| | printf format, "login:","description:" |
| | printf format, "---","---"} |
| | $7 ~ /\/bin\/bash/ {printf format, $1, $5}' /etc/passwd |
| | </code> |
| | |
| | ====Les fonctions mathématiques==== |
| | |
| | cos(x), exp(x), int(x), log(x), sin(x), sqrt(x), atan2(x,y), rand(x), srand(x) |
| |
| ====Exemples==== | ====Exemples==== |
| |
| * imprime chaque ligne du fichier /etc/passwd après avoir effacé le deuxième champs | * imprime chaque ligne du fichier /etc/passwd après avoir effacé le deuxième champs |
| <code> | <code bash> |
| awk -F ":" '{ $2 = "" ; print $0 }' /etc/passwd | awk -F ":" '{ $2 = "" ; print $0 }' /etc/passwd |
| </code> | </code> |
| |
| * imprime le nombre total de lignes du fichiers | * imprime le nombre total de lignes du fichiers |
| <code> | <code bash> |
| awk 'END {print NR}' /etc/passwd | awk 'END {print NR}' /etc/passwd |
| </code> | </code> |
| |
| * lire la 3ème ligne d'un fichier : | * lire la 3ème ligne d'un fichier : |
| <code> | <code bash> |
| awk '{if (NR==3) print}' <fichier> | awk '{if (NR==3) print}' <fichier> |
| </code> | </code> |
| |
| * imprime le dernier champs de chaque ligne | * imprime le dernier champs de chaque ligne |
| <code> | <code bash> |
| awk '{print $NF}' /etc/passwd | awk '{print $NF}' /etc/passwd |
| </code> | </code> |
| |
| * imprime le login et le temps de connexion. | * imprime le login et le temps de connexion. |
| <code> | <code bash> |
| who | awk '{print $1,$5}' | who | awk '{print $1,$5}' |
| </code> | </code> |
| |
| * imprime les lignes de plus de 75 caractères. (''print'' équivaut à ''print $0'') | * imprime les lignes de plus de 75 caractères. (''print'' équivaut à ''print $0'') |
| <code> | <code bash> |
| awk 'length($0)>75 {print}' /etc/passwd | awk 'length($0)>75 {print}' /etc/passwd |
| </code> | </code> |
| |
| * tests sur le fichier /etc/passwd : | * tests sur le fichier /etc/passwd : |
| <code> | <code bash> |
| awk 'BEGIN { print "Verification du fichier /etc/passwd pour ..."; | awk 'BEGIN { print "Verification du fichier /etc/passwd pour ..."; |
| print "- les utilisateurs avec UID = 0 " ; | print "- les utilisateurs avec UID = 0 " ; |
| |
| * supprimer le suffixe du domaine d'un nom de machine : | * supprimer le suffixe du domaine d'un nom de machine : |
| <code> | <code bash> |
| echo "toto.domaine.fr est un nom trop long !" | awk 'gsub(/\..*$/,"",$1) {print "machine="$1}' | echo "toto.domaine.fr est un nom trop long !" | awk 'gsub(/\..*$/,"",$1) {print "machine="$1}' |
| </code> | </code> |
| |
| | * afficher les blocs de texte du fichier FIC.txt compris entre les balises BEGIN et END : |
| | <code bash> |
| | awk '/BEGIN/,/END/' FILE.txt |
| | </code> |
| | |
| | * supprimer (ne pas afficher) les doublons de lignes dans un fichier : |
| | <code bash> |
| | awk '!x[$0]++' test.tmp |
| | </code> |
| | |
| | * quitter la boucle awk après le premier match |
| | <code bash> |
| | echo -e "toto\ntoto\n" | awk '/toto/ {print; exit}' |
| | toto |
| | </code> |
| | |
| | * émuler la fonction trim (efface les espaces avant et après une chaine) : |
| | <code bash> |
| | echo -e "toto :titi\n tata:tutu" | \ |
| | awk -F\: '{ sub(/^[ \t\r\n]+/, "", $1);sub(/[ \t\r\n]+$/, "", $1);print "\""$1"\""}' |
| | </code> |
| | |
| | * ne pas afficher les lignes en doublon |
| | <code bash> |
| | # count s'incrémente à chaque ligne déjà rencontrée ; on n'affiche donc la ligne qu'à sa première apparition |
| | awk '!(count[$0]++)' fic.txt |
| | </code> |
| | |
| | * compter le nombre de caractère dans une ligne (k pour l'exemple) |
| | <code bash> |
| | echo $ligne | awk -F'k' '{print NF-1}' |
| | </code> |
| | |
| | * N'afficher que les lignes paires |
| | <code bash> |
| | awk 'NR%2==0' fic.txt |
| | </code> |
| | |
| | * filtrer sans tenir compte de la casse (minuscule/majuscule = //case insensitive//): |
| | <code bash> |
| | echo "TOTO" | awk 'BEGIN {IGNORECASE=1} /toto/ {print}' |
| | TOTO |
| | </code> |