User Tools

Site Tools


informatique:linux:awk

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 <fichier> 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 “<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 “|” :
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) <tab>, <espace> ou <retour chariot> 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 <espace>
ORS séparateur d'enregistrement pour la sortie <retour chariot>
RLENGTH longueur de la chaine trouvée
RS séparateur d'enregistrement en entrée <retour chariot>
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 ® 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}' <fichier>
  • 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
informatique/linux/awk.txt · Last modified: 2022/04/13 13:03 by pteu