Démarrer avec awk

AWK par exemples

AWK est un langage de manipulation de chaînes, largement utilisé dans les systèmes UNIX. L’idée derrière AWK était de créer un langage polyvalent à utiliser lors du travail sur des fichiers, qui n’était pas trop complexe à comprendre.

AWK a quelques autres variantes, mais le concept principal est le même, juste avec des fonctionnalités supplémentaires. Ces autres variantes sont NAWK et GAWK. GAWK contient toutes les fonctionnalités des deux, tandis que NAWK est un pas au-dessus de AWK, si vous le souhaitez.

La façon la plus simple de penser à AWK est de considérer qu’il comporte 2 parties principales. Le motif et l’action.

Probablement l’exemple le plus basique d’AWK : (Voir aussi : Hello World)

BEGIN {print "START"}
      {print        }
END   {print "STOP" }

Ici, les mots-clés BEGIN et END sont le modèle, tandis que l’action est à l’intérieur du {}. Cet exemple serait inutile, mais il ne faudrait que des modifications mineures pour en faire une fonction utile.

BEGIN {print "File\tAuthor"}
      {print $8, "\t", $3}
END {print " - DONE - "}

Ici, \t représente un caractère de tabulation et est utilisé pour égaliser les limites de la ligne de sortie. $8 et $3 sont similaires à l’utilisation que l’on voit dans Shell Scripts, mais au lieu d’utiliser les 3e et 8e arguments, il utilise les 3e et 8e colonnes de la ligne d’entrée.

Ainsi, cet exemple afficherait : File Author sur la première ligne, tandis que la deuxième ligne concerne les chemins de fichiers. $8 est le nom du fichier, $3 est le propriétaire (en regardant le chemin du répertoire, ce sera plus clair). Enfin, la ligne du bas s’imprimerait, comme vous vous en doutez - TERMINÉ -

Le crédit pour l’exemple ci-dessus va à http://www.grymoire.com/Unix/Awk.html

Fichier de référence

coins.txt de Greg Goebel :

gold     1    1986  USA                 American Eagle
gold     1    1908  Austria-Hungary     Franz Josef 100 Korona
silver  10    1981  USA                 ingot
gold     1    1984  Switzerland         ingot
gold     1    1979  RSA                 Krugerrand
gold     0.5  1981  RSA                 Krugerrand
gold     0.1  1986  PRC                 Panda
silver   1    1986  USA                 Liberty dollar
gold     0.25 1986  USA                 Liberty 5-dollar piece
silver   0.5  1986  USA                 Liberty 50-cent piece
silver   1    1987  USA                 Constitution dollar
gold     0.25 1987  USA                 Constitution 5-dollar piece
gold     1    1988  Canada              Maple Leaf

Théorie minimale

General awk one-liner:

awk <awk program> <file>

ou:

<shell-command> | awk <awk program> 

<shell-command> et <file> sont adressés comme awk input.

<awk program> est un code suivant ce modèle (guillemets simples et non doubles) :

'BEGIN   {<init actions>};
 <cond1> {<program actions>};
 <cond2> {<program actions>};
 ...
 END  {<final actions>}'

où:

  • La condition <condX> est le plus souvent une expression régulière /re/, à mettre en correspondance avec les lignes d’entrée awk ;
  • <* actions> sont des séquences de statements, similaires aux commandes shell, équipées de constructions de type C.

`` est traité selon les règles suivantes :
  1. BEGIN ... et END ... sont facultatifs et exécutés avant ou après le traitement des lignes d’entrée awk.
  2. Pour chaque ligne de l’entrée awk, si la condition <condN> est meat, alors le bloc <program actions> associé est exécuté.
  3. {<actions de programme>} par défaut est {print $0}.

Conditions peut être combiné avec des opérateurs logiques standards :

    /gold/ || /USA/ && !/1986/

&& a priorité sur || ;

__La déclaration print __. L’instruction print item1 item2 imprime les éléments sur STDOUT. Les éléments peuvent être des variables (X, $0), des chaînes (“bonjour”) ou des nombres. item1, item2 sont collationnés avec la valeur de la variable OFS ; item1 item2 sont juste apoxed ! Utilisez item1 " " item2 pour les espaces ou printf pour plus de fonctionnalités.

Variables n’ont pas besoin de $, c’est-à-dire : print myVar; Les variables spéciales suivantes sont intégrées dans awk :

  • FS : agit comme un séparateur de champs pour diviser les lignes d’entrée awk en champs. Je peux être un seul caractère, FS="c" ; une chaîne nulle, FS="" (chaque caractère individuel devient alors un champ séparé) ; une expression régulière sans slash, FS="re" ; FS=" " représente des séries d’espaces et de tabulations et est la valeur par défaut.
  • ‘NF’ : le nombre de champs à lire ;
  • $1, $2, … : 1er champ, 2e champ. etc. de la ligne d’entrée actuelle,
  • $0 : ligne d’entrée actuelle ;
  • NR : numéro de la ligne de vente actuelle.
  • OFS : chaîne pour assembler les champs lors de l’impression.
  • ORS : séparateur d’enregistrement de sortie, par défaut une nouvelle ligne.
  • RS : séparateur de ligne d’entrée (enregistrement). Par défaut, saut de ligne. Définir comme FS.
  • IGNORECASE : affecte FS et RS lorsqu’il s’agit d’expressions régulières ;

Exemples

Filtrez les lignes par regexp gold et comptez-les :

# awk 'BEGIN {print "Coins"} /gold/{i++; print $0}  END {print i " lines out of " NR}' coins.txt
Coins
gold     1    1986  USA                 American Eagle      
gold     1    1908  Austria-Hungary     Franz Josef 100 Korona 
gold     1    1984  Switzerland         ingot 
gold     1    1979  RSA                 Krugerrand 
gold     0.5  1981  RSA                 Krugerrand 
gold     0.1  1986  PRC                 Panda                       
gold     0.25 1986  USA                 Liberty 5-dollar piece
gold     0.25 1987  USA                 Constitution 5-dollar piece
gold     1    1988  Canada              Maple Leaf
9 lines out of 13

Action et condition print $0 par défaut basées sur la variable awk interne NR :

# awk 'BEGIN {print "First 3 coins"} NR<4' coins.txt
First 3 coins                                                  
gold     1    1986  USA                 American Eagle         
gold     1    1908  Austria-Hungary     Franz Josef 100 Korona 
silver  10    1981  USA                 ingot
Formatage avec `printf` de style C :
# awk '{printf ("%s \t %3.2f\n", $1, $2)}' coins.txt
gold     1.00                                      
gold     1.00                                      
silver   10.00                                     
gold     1.00                                      
gold     1.00                                      
gold     0.50                                      
gold     0.10                                      
silver   1.00                                      
gold     0.25                                      
silver   0.50                                      
silver   1.00                                      
gold     0.25                                      
gold     1.00

Exemples de conditions

awk 'NR % 6'            # prints all lines except those divisible by 6
awk 'NR > 5'            # prints from line 6 onwards (like tail -n +6, or sed '1,5d')
awk '$2 == "foo"'       # prints lines where the second field is "foo"
awk '$2 ~ /re/'         # prints lines where the 2nd field mateches the regex /re/
awk 'NF >= 6'           # prints lines with 6 or more fields
awk '/foo/ && !/bar/'   # prints lines that match /foo/ but not /bar/
awk '/foo/ || /bar/'    # prints lines that match /foo/ or /bar/ (like grep -e 'foo' -e 'bar')
awk '/foo/,/bar/'       # prints from line matching /foo/ to line matching /bar/, inclusive
awk 'NF'                # prints only nonempty lines (or: removes empty lines, where NF==0)
awk 'NF--'              # removes last field and prints the line

En ajoutant une action {...}, on peut imprimer un champ spécifique, plutôt que la ligne entière, par exemple :

awk '$2 ~ /re/{print $3 " " $4}'

imprime les troisième et quatrième champs de lignes où le deuxième champ correspond à la regex /re/.

Certaines fonctions de chaîne

Fonction substr() :

# awk '{print substr($3,3) " " substr($4,1,3)}' 
86 USA                                            
08 Aus                                            
81 USA                                            
84 Swi                                            
79 RSA                                            
81 RSA                                            
86 PRC                                            
86 USA                                            
86 USA                                            
86 USA                                            
87 USA                                            
87 USA                                            
88 Can                                            

match(s, r [, arr]) renvoie la position dans s où la regex r apparaît et définit les valeurs de RSTART et RLENGTH. Si l’argument “arr” est fourni, il renvoie le tableau arr où les éléments sont définis sur la sous-expression entre parenthèses correspondante. Le 0ème élément correspond à arr est défini sur la correspondance entière de regex. De plus, les expressions arr[n, "start"] et arr[n, "length"] fournissent la position de départ et la longueur de chaque sous-chaîne correspondante.

Plus de fonctions de chaîne :

sub(/regexp/, "newstring"[, target])
gsub(/regexp/, "newstring"[, target])
toupper("string")
tolower("string")

Déclarations

Une déclaration simple est souvent l’une des suivantes :

variable = expression 
print [ expression-list ] 
printf format [ , expression-list ] 
next # skip remaining patterns on this input line
exit # skip the rest of the input

Si stat1 et stat2 sont des instructions, les suivantes sont également des instructions :

{stat}

{stat1;  stat2}

{stat1 
stat2}

if ( conditional ) statement [ else statement ]

Les constructions de type C standard suivantes sont des instructions :

if ( conditional ) statement [ else statement ]
while ( conditional ) statement
for ( expression ; conditional ; expression ) statement
break    # usual C meaning 
continue # usual C meaning 

Une boucle de style C pour imprimer l’élément de description de longueur variable, en commençant par le champ 4 :

# awk '{out=""; for(i=4;i<=NF;i++){out=out" "$i}; print out}' coins.txt
USA American Eagle                    
Austria-Hungary Franz Josef 100 Korona
USA ingot                             
Switzerland ingot                     
RSA Krugerrand                        
RSA Krugerrand                        
PRC Panda                             
USA Liberty dollar                    
USA Liberty 5-dollar piece            
USA Liberty 50-cent piece             
USA Constitution dollar               
USA Constitution 5-dollar piece       
Canada Maple Leaf

Notez que i est initialisé à 0.

Si les conditions et les calculs s’appliquent aux champs numériques :

# awk '/gold/ {if($3<1980) print $0 "$" 425*$2}' coins.txt    
gold     1    1908  Austria-Hungary     Franz Josef 100 Korona      $425
gold     1    1979  RSA                 Krugerrand                  $425   

Script exécutable AWK

#!/usr/bin/gawk -f
# This is a comment
(pattern) {action}
...

Passage de variables shell

# var="hello"
# awk -v x="$var" 'BEGIN {print x}'
hello

Bonjour le monde

L’exemple Hello world est aussi simple que :

awk 'BEGIN {print "Hello world"}'

Le programme awk le plus basique consiste en une valeur vraie (généralement 1) et fait awk renvoyer son entrée :

$ date | awk '1'
Mon Jul 25 11:12:05 CEST 2016

Étant donné que “hello world” est également une valeur vraie, vous pouvez également dire :

$ date | awk '"hello world"'
Mon Jul 25 11:12:05 CEST 2016

Cependant, votre intention devient beaucoup plus claire si vous écrivez

$ date | awk '{print}'
Mon Jul 25 11:12:05 CEST 2016

Au lieu.

Comment exécuter des programmes AWK

Si le programme est court, vous pouvez l’inclure dans la commande qui exécute awk :

awk -F: '{print $1, $2}' /etc/passwd

Dans cet exemple, en utilisant le commutateur de ligne de commande -F:, nous conseillons à awk d’utiliser : comme délimiteur de champs d’entrée. Est-ce la même chose que

awk 'BEGIN{FS=":"}{print $1,$2}' file

Alternativement, nous pouvons enregistrer tout le code awk dans un fichier awk et appeler ce programme awk comme ceci :

awk -f 'program.awk' input-file1 input-file2 ...

program.awk peut être n’importe quel programme multiligne, c’est-à-dire :

# file print_fields.awk
BEGIN {print "this is a header"; FS=":"}
{print $1, $2}
END {print "that was it"}

Et puis lancez-le avec:

awk -f print_fields.awk /etc/passwd   #-f advises awk which program file to load

Ou Plus généralement :

awk -f program-file input-file1 input-file2 ...

L’avantage d’avoir le programme dans un fichier séparé est que vous pouvez écrire le programme avec une identification correcte pour avoir un sens, vous pouvez inclure des commentaires avec # , etc.