======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