|
Il y a actuellement 4 visiteurs connectés sur le site !
Introduction à (g)awk
Présentation
Awk est destiné à manipuler des fichiers textes.
Le principe est de chercher dans des fichiers certaines lignes, ou du texte qui contient des motifs. Quand une ligne correspond à un des motifs, awk effectue certaines actions jusqu'à ce qu'il ait atteint la fin du ou des fichiers.
Quand awk est executé, vous demandez au programme awk ce qu'il va faire pour vous. Le programme contient un certain nombre de regles. (des definitions de fonction ou des fonctionnalités plus compliquées). Chaque regle indique un motif à chercher et une action à effectuer une fois le motif trouvé.
Nous allons pouvoir commencer:
Qu'est ce que c'est
awk est un langage interprété, installé par défaut sur les distributions linux (au moins rh et mandrake) dans le répertoire /bin.
BEGIN
|
Le bloc qui suit est exécuté au début du programme, une seule fois.
|
END
|
Le bloc qui suit est exécuté à la fin du programme, une seule fois.
|
/expression régulière/
|
Le bloc qui suit est exécuté si les données en cours de traitement correspondent à l'expression régulière.
Si vous ne connaissez pas les expressions régulières, lisez la page man egrep.
Un exemple : /^#/ Permet de selectionner les lignes commençant par un #.
|
expression relationnelle
|
Le bloc qui suit est exécuté si l'expression est vraie.
Un exemple : maVariable==5
|
modèle && modèle
|
Le bloc qui suit est exécuté si les deux modèles sont vérifiés.
|
modèle || modèle
|
Le bloc qui suit est exécuté si au moins un des modèles est vérifié.
|
modèle0 ? modèle1 : modèle2
|
Le bloc qui suit est exécuté si les modèles 0 et 1 sont vérifiés où si le modèle2 uniquement est vérifié.
|
(modèle)
|
Permet de grouper les modèles.
Un exemple : (modèle0 || modèle1) && modèle2
|
! modèle
|
Le bloc qui suit est exécuté si le modèle n'est pas vérifié.
|
modèle1, modèle2
|
Le bloc qui suit est exécuté pour la partie des données en cours commençant par modèle1 et finissant par modèle2.
Un exemple : /\/\*/, /\*\// pour afficher les commentaires multilignes
|
Exemples
Avec le programme
{ print $1 " moyenne " ($2 +$3)/2 }
et le le fichier :
BOBY 18 5
ZELYNOU 6 11
ANTIN 8 4
BOB 16 8 15
IZEL 16 18 12
on obtient comme affichage :
BOBY moyenne 11.5
ZELYNOU moyenne 8.5
ANTIN moyenne 6
BOB moyenne 12
IZEL moyenne 17
De même, avec le programme
{ print $1
printf(" moyenne %5.2f\n",($2+$3)/2 )
} # fin de traitement de la ligne courante
et le le fichier :
BOBY 18 5
ZELYNOU 6 11
ANTIN 8 4
BOB 16 8 15
IZEL 16 18 12
on obtient comme affichage :
BOBY
moyenne 11.50
ZELYNOU
moyenne 8.50
ANTIN
moyenne 6.50
BOB
moyenne 12.00
IZEL
moyenne 17.00
awk -F ":" '{ $2 = "" ; print $0 }' /etc/passwd imprime chaque ligne du fichier /etc/passwd après avoir effacé le deuxième champ
awk 'END {print NR}' fichier imprime le nombre total de lignes du fichiers
awk '{print $NF}' fichier imprime le dernier champs de chaque ligne
who | awk '{print $1,$5}' imprime le login et le temps de connexion.
awk 'length($0)>75 {print}' fichier imprime les lignes de plus de 75 caractères. (print équivaur à print $0)
# precede chaque ligne par son numéro pour ce fichier (alignement à gauche).
awk '{print FNR "\t" $0}' files*
# precede chaque ligne par son numéro Pour l'ensemble des fichiers, avec une tabulation.
awk '{print NR "\t" $0}' files*
# énumère chaque ligne d'un fichier (le nombre est à gauche, aligné à droite)
awk '{printf("%5d : %s\n", NR,$0)}'
# énumère chaque ligne du fichier, mais ecrit seulement les nombres si la ligne n'est pas vide
awk 'NF{$0=++a " :" $0};1'
awk '{print (NF? ++a " :" :"") $0}'
# compte les lignes (comme "wc -l")
awk 'END{print NR}'
# écrit la somme des champs de chaque ligne
awk '{s=0; for (i=1; i<=NF; i++) s=s+$i; print s}'
# ajoute tous les champs de toutes les lignes et affiche la somme
awk '{for (i=1; i<=NF; i++) s=s+$i}; END{print s}'
# affiche chaque ligne après avoir remplacé chaque champ par sa valeur absolue
awk '{for (i=1; i<=NF; i++) if ($i < 0) $i = -$i; print }'
awk '{for (i=1; i<=NF; i++) $i = ($i < 0) ? -$i : $i; print }'
# affiche le nombre total de champs ("mots") de toutes les lignes
awk '{ total = total + NF }; END {print total}' fichier
# affiche le nombre total de lignes qui contiennent "Beth"
awk '/Beth/{n++}; END {print n+0}' fichier
# affiche le premier champ le plus grand et la ligne qui le contient
awk '$1 > max {max=$1; maxline=$0}; END{ print max, maxline}'
# affiche le nombre de champs de chaque ligne, suivi par la ligne
awk '{ print NF ":" $0 } '
# affiche le dernier champ de chaque ligne
awk '{ print $NF }'
# affiche le dernier champ de la dernière ligne
awk '{ field = $NF }; END{ print field }'
# affiche toutes les lignes de plus de quatre champs
awk 'NF > 4'
# affiche toutes les lignes dans lesquelles la valeur du dernier champ est supèrieure à 4
awk '$NF > 4'
# crée une chaine d'une certaine longueur (c'est-à-dire avec 513 espaces)
awk 'BEGIN{while (a++<513) s=s " "; print s}'
# insére une chaine d'une certaine longueur à une certaine position de caractère
# Exemple: insére 49 espaces après la colonne 6 de chaque ligne en entrèe.
gawk --re-interval 'BEGIN{while(a++<49)s=s " "};{sub(/^.{6}/,"&" s)};1'
# crée un tableau nommé month, indexé par des numéros afin que month[1]
# soit 'Jan', month[2] 'Feb', month[3] 'Mar' et ainsi de suite.
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", month, " ")
# crée un tableau appelé "mdigit", indexé par des chaines, afin que
# mdigit["Jan"] soit 1, mdigit["Feb"] 2, etc. Necessite un tableau "month"
for (i=1; i<=12; i++) mdigit[month[i]] = i
# Dans un environnement UNIX, convertit des lignes DOS (CR/LF) en format Unix
awk '{sub(/\r$/,"")};1' # suppose que chaque ligne finit par Ctrl-M
# Dans un environnement UNIX convertit des lignes Unix (LF) en format DOS
awk '{sub(/$/,"\r")};1'
# Dans un environnement DOS convertit des lignes Unix (LF) en format DOS
awk 1
# Dans un environnement DOS convertit des lignes DOS (CR/LF) en format Unix
# Ne peut être fait avec les versions DOS de awk, autres que gawk:
gawk -v BINMODE="w" '1' infile >outfile
# supprimer les espaces principaux (espaces, tabulations) au début de chaque ligne
awk '{sub(/^[ \t]+/, "")};1'
# supprime les espaces finaux (espaces, tabulations) de la fin de chaque ligne
awk '{sub(/[ \t]+$/, "")};1'
# supprime les espaces au début et à la fin de chaque lignes
awk '{gsub(/^[ \t]+|[ \t]+$/,"")};1'
awk '{$1=$1};1' # supprime également des espaces supplémentaires entre les champs
# insére cinq espaces vides au début de chaque ligne (fait une page de décalage)
awk '{sub(/^/, " ")};1'
# aligne le texte à droite avec une largeur de 79-colonnes
awk '{printf "%79s\n", $0}' fichier*
# centre le texte avec une largeur de 79-caractères
awk '{l=length();s=int((79-l)/2); printf "%"(s+l)"s\n",$0}' fichier*
# remplace (trouve et remplace) "foo" par "bar" dans chaque ligne
awk '{sub(/foo/,"bar")}; 1' # remplace seulement la première occurence
gawk '{$0=gensub(/foo/,"bar",4)}; 1' # remplace seulement la quatrième occurence
awk '{gsub(/foo/,"bar")}; 1' # remplace toutes les occurences de la ligne
# remplace "foo" par "bar" seulement pour les lignes qui contiennent "baz"
awk '/baz/{gsub(/foo/, "bar")}; 1'
# remplace "foo" par "bar" sauf pour les lignes qui contiennent "baz"
awk '!/baz/{gsub(/foo/, "bar")}; 1'
# modifie "scarlet" ou "ruby" ou "puce" en "red"
awk '{gsub(/scarlet|ruby|puce/, "red")}; 1'
# inverser l'ordre des lignes (comme avec "tac")
awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' fichier*
# si une ligne se finit par un backslash, ajoutez la ligne suivante à celui-ci (cela échoue si il y a plusieurs lignes #finissant par un backslash...)
awk '/\\$/ {sub(/\\$/,""); getline t; print $0 t; next}; 1' fichier*
# affiche et trie les noms de login de tous les utilisateurs
awk -F ":" '{print $1 | "sort" }' /etc/passwd
# affiche les deux premiers champs, en ordre inverse de chaque ligne
awk '{print $2, $1}' file
# échange les deux premiers champs de chaque ligne
awk '{temp = $1; $1 = $2; $2 = temp}' fichier
# affiche chaque ligne, en supprimant le second champ de cette ligne
awk '{ $2 = ""; print }'
# affiche dans l'ordre inverse les champs de chaque ligne
awk '{for (i=NF; i>0; i--) printf("%s ",$i);print ""}' file
# concatene toutes les 5 lignes en entrée, en employant un séparateur virgule
# entre les champs
awk 'ORS=NR%5?",":"\n"' file
# affiche les 10 premières lignes du fichier (émule le comportement de la commande "head")
awk 'NR < 11'
# affiche la première ligne du fichier (émule "head -1")
awk 'NR>1{exit};1'
# affiche les deux dernières lignes d'un fichier (émule "tail -2")
awk '{y=x "\n" $0; x=$0};END{print y}'
# affiche la dernière ligne d'un fichier (émule "tail -1")
awk 'END{print}'
# affiche seulement les lignes qui correspondent à une expression régulière (émule "grep")
awk '/regex/'
# affiche seulement les lignes qui ne correspondent pas à une expression régulière (émule "grep -v")
awk '!/regex/'
# affiche toutes les lignes où le champ #5 est égal à "abc123"
awk '$5 == "abc123"'
# affiche seulement les lignes où le champ #5 n'est pas égal à "abc123"
# cela affichera aussi les lignes qui ont moins de 5 champs.
awk '$5 != "abc123"'
awk '!($5 == "abc123")'
# correspondance avec un champ à l'aide d'une expression régulière
awk '$7 ~ /^[a-f]/' # affiche la ligne si le champ #7 correspond à l'expression régulière
awk '$7 !~ /^[a-f]/' # affiche la ligne si le champ #7 ne correspond pas à l'expression régulière
# affiche la ligne immédiatement avant une expression, mais pas la ligne contenant l'expression
awk '/regex/{print x};{x=$0}'
awk '/regex/{print (NR==1 ? "match on line 1" : x)};{x=$0}'
# affiche la ligne immédiatement après l'expression, mais pas la ligne contenant l'expression
awk '/regex/{getline;print}'
# fait une recherche avec grep pour AAA et BBB et CCC (dans n'importe quel ordre sur la même ligne)
awk '/AAA/ && /BBB/ && /CCC/'
# fait une recherche avecc grep pour AAA et BBB et CCC (dans cet ordre)
awk '/AAA.*BBB.*CCC/'
# affiche seulement des lignes de 65 caractères ou plus
awk 'length > 64'
# affiche seulement des lignes de moins de 65 caractères
awk 'length < 64'
# affiche la partie du fichier depuis l'expression régulière jusqu'à la fin du fichier
awk '/regex/,0'
awk '/regex/,EOF'
# affiche la partie du fichier basée sur les numéros de ligne (lignes 8-12, inclus)
awk 'NR==8,NR==12'
# affiche la ligne numéro 52
awk 'NR==52'
awk 'NR==52 {print;exit}' # plus efficace avec de gros fichiers
# affiche une partie d'un fichier entre deux expressions régulières (inclus)
awk '/Iowa/,/Montana/' # sensible à la casse
# supprimer toutes les lignes vides d'un fichier (même chose que "grep '.' ")
awk NF
awk '/./'
# supprime des doublons et des lignes consécutives (émule "uniq")
awk 'a !~ $0; {a=$0}'
# supprime les doublons et les lignes non-consécutives
awk '!a[$0]++' # script plus concis
awk '!($0 in a){a[$0];print}' # script plus efficace
Les variables
Voici la liste des variables prédéfinies en awk.
| ARGC |
Cette variable contient le nombre d'arguments passés
au programme. (sans les options passées à awk)
|
| ARGV |
Nombre d'arguments de la ligne de commande.
|
| CONVFMT |
Le format par défaut pour l'affichage des nombres, "%.6g" par défaut
|
| ENVIRON |
Un tableau contenant les variables d'environnement. Exemple ENVIRON["PATH"] contient votre path.
|
| FIELDWIDTHS |
Pour le cas de traitement de données non délimités mais contenant des champs de largeur fixe.
Cette variable est de la forme largeur_champ_1 largeur_champ_2 largeur_champ_3 etc....
|
| FNR |
Le numéro de l'enregistrement actuellement en cours de traitement.
Un exemple : awk '{print FNR ": " $0}' un_fichier permet d'afficher le fichier avec les numéros de lignes.
Un autre exemple : awk '{print FNR ": " $0}' un_fichier | grep ^45: permet d'afficher la ligne 45 du
fichier un_fichier. On fait appel à grep, ce n'est plus du 100% awk mais c'est beau la diversité, non ?
|
| FS |
Le séparateur de champs. No comment !
|
| IGNORECASE |
En gros, permet de ne pas prendre en compte la casse lors des comparaisons de chaines de caractères entre elles où
avec des expressions régulières. Voir la page de manuel pour les détails.
|
| NF |
nombre de champs de l'enregistrement courant.
|
| NR |
nombre d'enregistrements deja lu.
|
| RS |
separateur d'enregistrement en entrée
|
Bien entendu, vous pouvez également définir les vôtres.
Exemples :
maVar=3 déclare et initialise la variable myVar à la valeur 3.
print maVar affiche le contenu de la variable maVar.
print $maVar affiche le champ n°maVar de l'enregistrement en cours (ici, équivaut à print $3).
print $(maVar-1) affiche le champ n°(maVar-1) de l'enregistrement en cours (ici, équivaut à print $2).
split("toto:tata:titi:tutu", arr, ":") initialise le tableau arr à ("toto","tata",...). On accède ensuite aux valeurs par arr[1], arr[2], ..., arr[4]
Les fonctions
l y en a de nombreuses :
* pour l'affichage formaté, de textes et de nombres. print printf ...
* pour les opérations habituelles sur les chaines de caractères, concaténation, substitution, remplacement, recherche ... gensub substr tolower index split ...
* pour les accés aux systèmes de fichiers et au système fflush close nextline system
* pour les fonctions mathématiques rand sin exp log ...
* pour la gestion du temps systime strftime
Les possibilités de formatage importantes à votre disposition sont définies dans la page de manuel de awk.
Et vous pouvez également définir vos fonctions grâce à la syntaxe : function nom_de_fonction(param1, param2, var_locale_1, var_locale_2) {
...
...
}
ou func nom_de_fonction(param1, param2 var_locale_1, var_locale_2) {
...
...
}
Voici un tableau des fonctions numeriques:
| Nom des fonctions |
signification |
| atan2(y,x) |
arctangente de x/y en redians dans l'interval -pi pi |
| cos(x) |
cosinus (en radians) |
| exp(x) |
exponentielle e à la puissance x |
| int(x) |
valeur entière |
| log(x) |
logarythme naturel |
| rand() |
nombre aléatoire entre 0 et 1 |
| sin(x) |
sinus (en radians) |
| sqrt(x) |
racine carrée |
| srand(x) |
reinitialiser le générateur de nombre aléatoire |
Les fonctions sur les chaines de caractères
Dans le tableau suivant :
s et t represente des chaines de caractères
r une expression régulière
i et n des entiers
| Nom des fonctions |
signification |
| gsub(r,s,t) |
sur la chaine t, remplace toutes les occurance 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 formattée suivant fmt |
|
| sub(r,s,t) |
comme gsub, mais remplce uniquement la première occurence |
|
| substr(s,i,n) |
retourne la sous chaine de s commencant en i et de taille n |
|
Blocs
Utilisation des blocs
Prenons un exemple :
#!/usr/bin/awk -f
BEGIN {
print "début du script"
maVariable=5
nbLignes=0
nbCommentaires=0
nbCommentaires2=0
print "maVariable vaut " maVariable
}
( /^#/ || /^$/ ) {
nbCommentaires++
}
{
nbLignes++
printf "%04d:%s\n",FNR,$0
if ( $0 ~ /^#/ || $0 ~ /^$/ )
nbCommentaires2++
}
}
END {
print "fin du script maVariable vaut toujours " maVariable
print "Le script a traité " FNR " lignes dont " nbCommentaires "(="co
mmentaires2 ") lignes vides ou commençant par un #"
}
Explications :
* Le bloc BEGIN est exécuté une fois au début, avant le traitement des données. Il affiche "début du script", initialise les variables et affiche le contenu de maVariable.
* Un bloc qui est exécuté pour chaque ligne commençant par un # ou vide, il incrémente la variable nbCommentaires.
* Un Bloc exécuté pour toutes les lignes, il incrémente la variable nbLignes, affiche la ligne préfixée par son numéro. Le numéro est formaté pour occupper quattre caractères. Les lignes suivantes (à partir du if) montrent l'équivalence entre le bloc du dessus et un test if dans le bloc principal. La variable nbCommentaires2 est incrémentée pour chaque ligne commençant par un dièse ou vide.
* Le bloc END est exécuté une fois à la fin, après le traitement des données. On voit que la variable maVariable est toujours définie et que son contenu n'a pas été altéré. Il indique également le nombre total de lignes traitées et le nombre de lignes de commentaires ou vides présentes dans le fichier.
Exécution br>
# chmod a+x ceScript.awk
# ./ceScript.awk unfichier.shell
début du script
maVariable vaut 5
0001:# Ce script affiche hello world
0002:
0003:echo "hello world"
0004:exit 0
fin du script maVariable vaut toujours 5
Le script a traité 4 lignes dont 2(=2) lignes vides ou commençant par un #
Utiliser awk
Par la ligne de commande
La structure de base de l'utilisation de awk à partir de la ligne de commande est la suivante:
awk [programme|-f fichierdecommandes] [options/variables] [fichiers]
Le premier argument peut être une commande awk, une série de commandes séparées par des points-virgules, ou bien encore un fichier contenant une série de commandes awk. S'il faut spécifier un fichier de commandes, vous devez utiliser l'option -f.
Les arguments suivants sur la ligne de commande sont les options ou les déclarations de variables. Si vous utilisez -F re, l'expression régulière re remplit le rôle de séparateur de champ à la place de "l'espace vide" par défaut. Pour initialiser des variables, saisissez variable =VALEUR dans la ligne de commande. Terminez par énumérer les fichiers d'entrée.
.
Des scripts exécutables
Pour des exemples simples de awk à l'intérieur de scripts shell, je vous renvoie sur :
http://abs.traduc.org/abs-3.2-fr/awk.html
Des filtres en exemple
Transformer le fichier hosts
.
Voici donc le code du premier filtre exemple :
#!/usr/bin/awk -f
!(/^#/ || /^$/) {
split ($1, array_ip, ".")
printf "%03d.%03d.%03d.%03d\t%s\t%s\n", array_ip[1], array_ip[2], array_ip[3], array_ip[4],$2,$3
}
La première ligne signifie qu'on va chercher awk dans le répertoire /usr/bin/, ce qui est le bon chemin pour ma slack.
La deuxiême ligne signifie qu'on ne s'intéresse qu'aux lignes qui ne commencent pas par un # ni aux lignes vides. En effet en expressions régulières, ^ signifie début de chaîne et $, fin de chaîne.
Par la suite, on découpe le premier paramètre en prenant comme séparateur le ., on stocke le résultat dans un tableau et on affiche les données du tableau en les formattant puis les autres champs (nom_de_machine et nom_de_machine.domaine). Le formatage %03d signifie qu'on affiche des entiers sur trois caractères et que s'il manque des caractères, on précède ceux qui existent avec des 0.
Il faut rendre ce script exécutable : chmod a+x prepare_hosts.awk
Puis, on l'appelle comme ça : ./prepare_hosts.awk /etc/hosts > hosts.prepared pour récupérer le fichier traité dans un fichier hosts.prepared placé dans le répertoire en cours.
.
Sources de l'article
|