======awk====== 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 }**. **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==== * une expression régulière : "/regexp/" ; "expression ~ /regexp/" ou sa négation "expression !~ /regexp/" Pour utiliser une variable bash comme regexp avec awk, on peut procéder ainsi : awk '$1 ~ /^'$VAR'/ {print}' * 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 combinaison des 3 séparés d'un opérateur booléen (|| ou &&) ====Les options==== * ''-f '' précise un fichier de script awk vi test.awk BEGIN{FS=";"; OFS=" : "} $0 ~ "test" {print $0} :x awk -f test.awk fichier.txt ceci est un test * ''-F ""'' spécifie le séparateur de champs (" " (espace) par défaut). NB : on peut spécifier plusieurs séparateurs en les séparant par "|" : echo "toto;tata-titi" | awk -F":|-" '{print $1" "$2" "$3}' toto tata titi * ''-v var="val"'' : permet d'affecter une valeur //val// à une variable //var// avant la partie BEGIN HOST=toto ; echo titi | awk -v h=$HOST '{print "h="h}' h=toto Autre façon de passer des variables dans un fichier de script : vi test.awk BEGIN{FS=";"; OFS=" : "} $0 ~ rechch {print $0} :x awk -f test.awk rechch=test fichier.txt ceci est un test ====Les variables prédéfinies==== ^ Variable ^ Description ^ Valeur par défaut ^ | ARGC | nombre d'arguments de la ligne de commande || | ARGV | tableau des arguments de la ligne de commande || | FILENAME | nom du fichier sur lequel on applique les commandes || | ENVIRON | tableau associatif des variables d'environnement || | FNR | nombre d'enregistrements parcourus du fichier courant || | FS | séparateur de champs (en entrée) | , ou contigus | | NF | nombre de champs de l'enregistrement courant || | NR | nombre d'enregistrements parcouru (tous fichiers confondus) || | OFMT | format de sortie des nombres || | OFS | séparateur de champs pour la sortie | | | ORS | séparateur d'enregistrement pour la sortie | | | RLENGTH | longueur de la chaine trouvée || | RS | séparateur d'enregistrement en entrée | | | RSTART | début de la chaine trouvée || | SUBSEP | séparateur de subscript || ====Les fonctions texte==== 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 occurrence de r par 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 * ''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 * ''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 * ''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 : # 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 ====Les fonctions mathématiques==== cos(x), exp(x), int(x), log(x), sin(x), sqrt(x), atan2(x,y), rand(x), srand(x) ====Exemples==== * imprime chaque ligne du fichier /etc/passwd après avoir effacé le deuxième champs awk -F ":" '{ $2 = "" ; print $0 }' /etc/passwd * imprime le nombre total de lignes du fichiers awk 'END {print NR}' /etc/passwd * lire la 3ème ligne d'un fichier : awk '{if (NR==3) print}' * imprime le dernier champs de chaque ligne awk '{print $NF}' /etc/passwd * imprime le login et le temps de connexion. who | awk '{print $1,$5}' * imprime les lignes de plus de 75 caractères. (''print'' équivaut à ''print $0'') awk 'length($0)>75 {print}' /etc/passwd * tests sur le 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 * supprimer le suffixe du domaine d'un nom de machine : echo "toto.domaine.fr est un nom trop long !" | awk 'gsub(/\..*$/,"",$1) {print "machine="$1}' * afficher les blocs de texte du fichier FIC.txt compris entre les balises BEGIN et END : awk '/BEGIN/,/END/' FILE.txt * supprimer (ne pas afficher) les doublons de lignes dans un fichier : awk '!x[$0]++' test.tmp * quitter la boucle awk après le premier match echo -e "toto\ntoto\n" | awk '/toto/ {print; exit}' toto * émuler la fonction trim (efface les espaces avant et après une chaine) : echo -e "toto :titi\n tata:tutu" | \ awk -F\: '{ sub(/^[ \t\r\n]+/, "", $1);sub(/[ \t\r\n]+$/, "", $1);print "\""$1"\""}' * ne pas afficher les lignes en doublon # 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 * compter le nombre de caractère dans une ligne (k pour l'exemple) echo $ligne | awk -F'k' '{print NF-1}' * N'afficher que les lignes paires awk 'NR%2==0' fic.txt