GNU/Linux >> Tutoriels Linux >  >> Linux

Premiers pas avec la commande AWK [Guide du débutant]

La commande AWK remonte aux premiers jours d'Unix. Il fait partie de la norme POSIX et devrait être disponible sur n'importe quel système de type Unix. Et au-delà.

Bien que parfois discrédité en raison de son ancienneté ou de son manque de fonctionnalités par rapport à un langage polyvalent comme Perl, AWK reste un outil que j'aime utiliser dans mon travail quotidien. Parfois pour écrire des programmes relativement complexes, mais aussi à cause des puissants one-liners que vous pouvez écrire pour résoudre les problèmes avec vos fichiers de données.

Donc, c'est exactement le but de cet article. Vous montrant comment vous pouvez tirer parti de la puissance AWK en moins de 80 caractères pour effectuer des tâches utiles. Cet article n'est pas destiné à être un didacticiel AWK complet, mais j'ai quand même inclus quelques commandes de base au début, donc même si vous avez peu ou pas d'expérience, vous pouvez saisir les concepts de base d'AWK.

Mes exemples de fichiers pour ce tutoriel AWK

Tous les one-liners décrits dans cet article seront testés sur le même fichier de données :

cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Vous pouvez obtenir une copie de ce fichier en ligne sur GitHub.

Connaître les variables prédéfinies et automatiques dans AWK

AWK prend en charge quelques variables prédéfinies et automatiques pour vous aider à écrire vos programmes. Parmi eux, vous rencontrerez souvent :

RSLe séparateur d'enregistrements. AWK traite vos données un enregistrement à la fois. Le séparateur d'enregistrements est le délimiteur utilisé pour diviser le flux de données d'entrée en enregistrements. Par défaut, il s'agit du caractère de saut de ligne. Donc, si vous ne le modifiez pas, un enregistrement correspond à une ligne du fichier d'entrée.

NRLe numéro d'enregistrement d'entrée actuel. Si vous utilisez le délimiteur de saut de ligne standard pour vos enregistrements, il correspond au numéro de ligne d'entrée actuel.

FS/OFSLe(s) caractère(s) utilisé(s) comme séparateur de champ. Une fois qu'AWK a lu un enregistrement, il le divise en différents champs en fonction de la valeur de FS . Lorsque AWK imprime un enregistrement sur la sortie, il rejoint les champs, mais cette fois, en utilisant l'OFS séparateur au lieu du FS séparateur. Généralement, FS et OFS sont les mêmes, mais ce n'est pas obligatoire. "espace blanc" est la valeur par défaut pour les deux.

NF – Le nombre de champs dans l'enregistrement en cours. Si vous utilisez le délimiteur standard "espace blanc" pour vos champs, cela correspondra au nombre de mots dans l'enregistrement actuel.

Il existe d'autres variables AWK plus ou moins standard disponibles, il vaut donc la peine de consulter votre manuel d'implémentation AWK particulier pour plus de détails. Cependant, ce sous-ensemble est déjà suffisant pour commencer à écrire des one-liners intéressants.

A. Utilisation de base de la commande AWK

1. Imprimer toutes les lignes

Cet exemple est la plupart du temps inutile, mais ce sera néanmoins une bonne introduction à la syntaxe AWK :

awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Les programmes AWK sont constitués d'un ou plusieurs pattern { action } déclarations.

Si, pour un enregistrement donné ("ligne") du fichier d'entrée, le motif évalue à une valeur non nulle (équivalente à "true" dans AWK), les commandes dans le bloc d'action correspondant sont exécutés. Dans l'exemple ci-dessus, puisque 1 est une constante non nulle, le { print } bloc d'action est exécuté pour chaque enregistrement d'entrée.

Une autre astuce est { print } est le bloc d'action par défaut qui sera utilisé par AWK si vous n'en spécifiez pas explicitement un. Ainsi, la commande ci-dessus peut être raccourcie en :

awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Presque aussi inutile, le programme AWK suivant consommera son entrée mais ne produira rien en sortie :

awk 0 file

2. Supprimer un en-tête de fichier

awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

N'oubliez pas que cela équivaut à écrire explicitement :

awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Ce one-liner écrira des enregistrements du fichier d'entrée à l'exception du tout premier puisque dans ce cas la condition est 1>1 ce qui n'est évidemment pas vrai.

Étant donné que ce programme utilise les valeurs par défaut pour RS , en pratique, il supprimera la première ligne du fichier d'entrée.

3. Imprimer des lignes dans une plage

Ceci n'est qu'une généralisation de l'exemple précédent, et il ne mérite pas beaucoup d'explications, si ce n'est de dire && est le and logique opérateur :

awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team

4. Suppression des lignes d'espacement uniquement

awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support
17,05 apr 2019,abhishek,guest

AWK divise chaque enregistrement en champs, en fonction du séparateur de champs spécifié dans le FS variable. Le séparateur de champ par défaut est un ou plusieurs caractères d'espace blanc (alias, espaces ou tabulations). Avec ces paramètres, tout enregistrement contenant au moins un caractère non blanc contiendra au moins un champ.

Autrement dit, le seul cas où NF est 0 ("faux") lorsque l'enregistrement ne contient que des espaces. Ainsi, cette ligne n'imprimera que les enregistrements contenant au moins un caractère autre qu'un espace.

5. Suppression de toutes les lignes vides

awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support

17,05 apr 2019,abhishek,guest

Ce one-liner est basé sur une obscure règle POSIX qui spécifie si le RS est défini sur la chaîne vide, "alors les enregistrements sont séparés par des séquences composées d'un plus une ou plusieurs lignes vides."

Il convient de mentionner dans la terminologie POSIX qu'une ligne vide est une ligne complètement vide. Les lignes qui ne contiennent que des espaces blancs ne comptent pas comme "vides".

6. Extraction des champs

C'est probablement l'un des cas d'utilisation les plus courants pour AWK :extraire certaines colonnes du fichier de données.

awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
,
,
17,abhishek

Ici, j'ai défini explicitement les séparateurs de champs d'entrée et de sortie sur la virgule. Lorsque AWK divise un enregistrement en champs, il stocke le contenu du premier champ dans $1, le contenu du deuxième champ dans $2 et ainsi de suite. Je ne l'utilise pas ici, mais il convient de mentionner que 0 $ correspond à l'intégralité de l'enregistrement.

Dans ce one-liner, vous avez peut-être remarqué que j'utilise un bloc d'action sans motif. Dans ce cas, 1 ("vrai") est supposé pour le modèle, donc le bloc d'action est exécuté pour chaque enregistrement.

Selon vos besoins, il se peut qu'il ne produise pas ce que nous voudrions pour les lignes vides ou contenant uniquement des espaces. Dans ce cas, cette deuxième version pourrait être un peu meilleure :

awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

Dans les deux cas, j'ai passé des valeurs personnalisées pour FS et OFS sur la ligne de commande. Une autre option serait d'utiliser un BEGIN spécial bloc à l'intérieur du programme AWK pour initialiser ces variables avant la lecture du premier enregistrement. Ainsi, selon vos goûts, vous préférerez peut-être écrire plutôt :

awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

Il convient de mentionner ici que vous pouvez également utiliser END blocs pour effectuer certaines tâches après la lecture du dernier enregistrement. Comme nous allons le voir tout à l'heure. Cela étant dit, j'admets que c'est loin d'être parfait puisque les lignes ne contenant que des espaces blancs ne sont pas gérées avec élégance. Nous verrons bientôt une solution possible, mais avant cela, faisons quelques calculs…

7. Effectuer des calculs par colonne

AWK prend en charge les opérateurs arithmétiques standard. Et convertira automatiquement les valeurs entre le texte et les nombres en fonction du contexte. Vous pouvez également utiliser vos propres variables pour stocker des valeurs intermédiaires. Tout cela permet d'écrire des programmes compacts pour effectuer des calculs sur des colonnes de données :

awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263

Ou, de manière équivalente, en utilisant le += syntaxe abrégée :

awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263

Veuillez noter que les variables AWK n'ont pas besoin d'être déclarées avant utilisation. Une variable indéfinie est supposée contenir la chaîne vide. Ce qui, selon les règles de conversion de type AWK, est égal au nombre 0. Grâce à cette fonctionnalité, je n'ai pas pris la peine de gérer explicitement le cas où $1 contient du texte (dans le titre), des espaces blancs ou simplement rien. Dans tous ces cas, il comptera comme 0 et n'interférera pas avec notre sommation. Bien sûr, ce serait différent si j'effectuais des multiplications à la place. Alors, pourquoi n'utiliseriez-vous pas la section des commentaires pour suggérer une solution à ce cas ?

8. Compter le nombre de lignes non vides

J'ai déjà mentionné le END règle avant. Voici une autre application possible pour compter le nombre de lignes non vides dans un fichier :

awk '/./ { COUNT+=1 } END { print COUNT }' file
9

Ici, j'ai utilisé le COUNT variable et l'incrémente (+=1 ) pour chaque ligne correspondant à l'expression régulière /./ . C'est-à-dire chaque ligne contenant au moins un caractère. Enfin, le bloc END permet d'afficher le résultat final une fois que tout le fichier a été traité. Il n'y a rien de spécial à propos du nom COUNT . J'aurais pu utiliser Count , count , n , xxxx ou tout autre nom respectant les règles de nommage des variables AWK

Cependant, ce résultat est-il correct ? Eh bien, cela dépend de votre définition d'une ligne "vide". Si vous considérez que seules les lignes vides (selon POSIX) sont vides, alors c'est correct. Mais peut-être préféreriez-vous également considérer les lignes contenant uniquement des espaces blancs comme vides ?

awk 'NF { COUNT+=1 } END { print COUNT }' file
8

Cette fois, le résultat est différent puisque cette version ultérieure ignore également les lignes contenant uniquement des espaces, alors que la version initiale n'ignorait que les lignes vides. Pouvez-vous voir la différence? Je vous laisse comprendre cela par vous-même. N'hésitez pas à utiliser la section des commentaires si ce n'est pas assez clair !

Enfin, si vous n'êtes intéressé que par les lignes de données, et compte tenu de mon fichier de données d'entrée particulier, je pourrais écrire cela à la place :

awk '+$1 { COUNT+=1 } END { print COUNT }' file
7

Cela fonctionne grâce aux règles de conversion de type AWK. Le plus unaire dans le modèle force l'évaluation de $1 dans un contexte numérique. Dans mon fichier, les enregistrements de données contiennent un nombre dans leur premier champ. Les enregistrements non-données (en-tête, lignes vides, lignes d'espacement uniquement) contiennent du texte ou rien. Tous étant égaux à 0 lorsqu'ils sont convertis en nombres.

Notez qu'avec cette dernière solution, un enregistrement pour un utilisateur ayant finalement 0 crédits serait également supprimé.

B. Utilisation de tableaux dans AWK

Les tableaux sont une fonctionnalité puissante d'AWK. Tous les tableaux dans AWK sont des tableaux associatifs, ils permettent donc d'associer une chaîne arbitraire à une autre valeur. Si vous connaissez d'autres langages de programmation, vous les connaissez peut-être sous le nom de hachages , tableaux associatifs , dictionnaires ou cartes .

9. Un exemple simple de tableau AWK

Imaginons que je veuille connaître le crédit total pour tous les utilisateurs. Je peux stocker une entrée pour chaque utilisateur dans un tableau associatif, et chaque fois que je rencontre un enregistrement pour cet utilisateur, j'incrémente la valeur correspondante stockée dans le tableau.

awk '+$1 { CREDITS[$3]+=$1 }
     END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109

J'avoue que ce n'est plus un one-liner. Principalement à cause du for boucle utilisée pour afficher le contenu du tableau après traitement du fichier. Revenons maintenant à des exemples plus courts :

10. Identification des lignes en double à l'aide d'AWK

Les tableaux, tout comme les autres variables AWK, peuvent être utilisés à la fois dans les blocs d'action et dans les modèles. En profitant de cela, nous pouvons écrire une ligne pour n'imprimer que les lignes en double :

awk 'a[$0]++' file
52,01    dec   2018,sonia,team

Le ++ operator est l'opérateur post-incrément hérité de la famille des langages C (dont AWK est un fier membre, grâce à Brian Kernighan qui a été l'un de ses auteurs originaux).

Comme son nom l'indique, l'opérateur de post-incrémentation incrémente ("add 1") une variable, mais seulement après que sa valeur a été prise pour l'évaluation de l'expression englobante.

Dans ce cas, a[$0] est évalué pour voir si l'enregistrement sera imprimé ou non, et une fois la décision prise, dans tous les cas, l'entrée du tableau est incrémentée.

Ainsi, la première fois qu'un enregistrement est lu, a[$0] est indéfini, et donc équivalent à zéro pour AWK. Ainsi, ce premier enregistrement n'est pas écrit sur la sortie. Ensuite, cette entrée est changée de zéro à un.
La deuxième fois que le même enregistrement d'entrée est lu, a[$0] est maintenant 1. C'est "vrai". La ligne sera imprimée. Cependant, avant cela, l'entrée du tableau est mise à jour de 1 à 2. Et ainsi de suite.

11. Suppression des lignes en double

En corollaire du one-liner précédent, nous voudrons peut-être supprimer les lignes en double :

awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support


17,05 apr 2019,abhishek,guest

La seule différence est l'utilisation de l'opérateur logique et non (! ) qui inversent la valeur de vérité de l'expression. Ce qui était faux devient vrai, et ce qui était vrai devient faux. Les non logiques n'ont absolument aucune influence sur le ++ incrémentation de poste qui fonctionne exactement comme avant.

C. Magie des séparateurs de champs et d'enregistrements

12. Changer les séparateurs de champs

awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Ce programme définit le FS et OFS variable pour utiliser une virgule comme séparateur de champ d'entrée et un point-virgule comme séparateur de champ de sortie. Puisque AWK ne change pas l'enregistrement de sortie tant que vous n'avez pas changé de champ, le $1=$1 L'astuce est utilisée pour forcer AWK à battre le record et à le réassembler à l'aide du séparateur de champ de sortie.

Rappelez-vous ici que le bloc d'action par défaut est { print } . Vous pouvez donc réécrire cela plus explicitement comme :

awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Vous avez peut-être remarqué que ces deux exemples suppriment également les lignes vides. Pourquoi? Eh bien, souvenez-vous des règles de conversion AWK :une chaîne vide est "fausse". Toutes les autres chaînes sont "vraies". L'expression $1=$1 est une affectation qui modifie $1 . Cependant, c'est aussi une expression. Et il évalue à la valeur de $1 –qui est "faux" pour la chaîne vide. Si vous voulez vraiment toutes les lignes, vous devrez peut-être écrire quelque chose comme ça à la place :

awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

Vous souvenez-vous du && opérateur? C'était le ET logique. || est le OU logique. La parenthèse est nécessaire ici en raison des règles de priorité des opérateurs. Sans eux, le modèle aurait été interprété à tort comme $1=($1 || 1) Au lieu. Je vous laisse comme exercice pour tester en quoi le résultat aurait alors été différent.

Enfin, si vous n'aimez pas trop l'arithmétique, je parie que vous préférerez cette solution plus simple :

awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

13. Suppression de plusieurs espaces

awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest

C'est presque le même programme que le précédent. Cependant, j'ai laissé les séparateurs de champs à leurs valeurs par défaut. Ainsi, plusieurs espaces blancs sont utilisés comme séparateur de champ d'entrée, mais un seul espace est utilisé comme séparateur de champ de sortie. Cela a le bel effet secondaire de fusionner multiples espaces blancs en un espace.

14. Joindre des lignes à l'aide d'AWK

Nous avons déjà utilisé OFS , le séparateur de champ de sortie. Comme vous l'avez peut-être deviné, il a le ORS contrepartie pour spécifier le séparateur d'enregistrement de sortie :

awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle    abhishek

Ici, j'ai utilisé un espace après chaque enregistrement au lieu d'un caractère de saut de ligne. Ce one-liner est suffisant dans certains cas d'utilisation, mais il a encore quelques inconvénients.

De toute évidence, il ne supprime pas les lignes contenant uniquement des espaces blancs (les espaces supplémentaires après öle viennent de là). Donc, je peux finir par utiliser une expression régulière simple à la place :

awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek

C'est mieux maintenant, mais il y a toujours un problème possible. Ce sera plus évident si nous changeons le séparateur en quelque chose de visible :

awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+

Il y a un séparateur supplémentaire à la fin de la ligne, car le séparateur de champ est écrit après chaque enregistrement. Y compris le dernier.

Pour résoudre ce problème, je vais réécrire le programme pour afficher un séparateur personnalisé avant l'enregistrement, en commençant par le deuxième enregistrement de sortie.

awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek

Comme je m'occupe d'ajouter moi-même le séparateur, j'ai également défini le séparateur d'enregistrement de sortie AWK standard sur la chaîne vide. Cependant, lorsque vous commencez à vous occuper des séparateurs ou du formatage, c'est peut-être le signe que vous devriez envisager d'utiliser le printf fonction au lieu de la fonction print déclaration. Comme nous allons le voir maintenant.

D. Formatage des champs

J'ai déjà mentionné la relation entre les langages de programmation AWK et C. Entre autres choses, de la bibliothèque standard du langage C, AWK hérite du puissant printf fonction, permettant un grand contrôle sur la mise en forme du texte envoyé à la sortie.

Le printf La fonction prend un format comme premier argument, contenant à la fois du texte brut qui sera généré textuellement et des caractères génériques utilisés pour formater différentes sections de la sortie. Les jokers sont identifiés par le % personnage. Le plus courant étant %s (pour le formatage des chaînes), %d (pour le formatage des nombres entiers) et %f (pour le formatage des nombres à virgule flottante). Comme cela peut être assez abstrait, voyons un exemple :

awk '+$1 { printf("%s ",  $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek

Vous remarquerez peut-être, à l'opposé de l'print déclaration, le printf la fonction n'utilise pas OFS et ORS valeurs. Donc, si vous voulez un séparateur, vous devez le mentionner explicitement comme je l'ai fait en ajoutant un espace à la fin de la chaîne de format. C'est le prix à payer pour avoir le contrôle total de la sortie.

Bien qu'il ne s'agisse pas du tout d'un spécificateur de format, c'est une excellente occasion d'introduire le \n notation qui peut être utilisée dans n'importe quelle chaîne AWK pour représenter un caractère de saut de ligne.

awk '+$1 { printf("%s\n",  $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek

15. Produire des résultats tabulaires

AWK applique un format de données d'enregistrement/de champ basé sur des délimiteurs. Cependant, en utilisant le printf fonction, vous pouvez également produire une sortie tabulaire à largeur fixe. Parce que chaque spécificateur de format dans un printf L'instruction peut accepter un paramètre de largeur facultatif :

awk '+$1 { printf("%10s | %4d\n",  $3, $1) }' FS=, file
   sylvain |   99
     sonia |   52
     sonia |   52
     sonia |   25
   sylvain |   10
       öle |    8
  abhishek |   17

Comme vous pouvez le voir, en spécifiant la largeur de chaque champ, AWK les remplit à gauche avec des espaces. Pour le texte, il est généralement préférable de remplir à droite, ce qui peut être réalisé en utilisant un nombre de largeur négatif. De plus, pour les nombres entiers, nous aimerions peut-être remplir les champs avec des zéros au lieu d'espaces. Ceci peut être obtenu en utilisant un 0 explicite avant la largeur du champ :

awk '+$1 { printf("%-10s | %04d\n",  $3, $1) }' FS=, file
sylvain    | 0099
sonia      | 0052
sonia      | 0052
sonia      | 0025
sylvain    | 0010
öle        | 0008
abhishek   | 0017

16. Traiter les nombres à virgule flottante

Le %f le format ne mérite pas beaucoup d'explications…

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429

… sauf peut-être pour dire que vous voulez presque toujours définir explicitement la largeur et la précision du champ du résultat affiché :

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG=  37.6

Ici, la largeur du champ est de 6, ce qui signifie que le champ occupera l'espace de 6 caractères (y compris le point, et éventuellement complété par des espaces à gauche comme d'habitude). La précision .1 signifie que nous voulons afficher le nombre avec 1 nombre décimal après le point. Je vous laisse deviner quoi %06.1 s'afficherait à la place.

E. Utilisation des fonctions de chaîne dans AWK

En plus du printf fonction, AWK contient quelques autres fonctions de manipulation de chaînes intéressantes. Dans ce domaine, les implémentations modernes comme Gawk ont ​​un ensemble plus riche de fonctions internes au prix d'une portabilité moindre. Pour ma part, je m'en tiendrai ici à quelques fonctions définies par POSIX qui devraient fonctionner de la même manière partout.

17. Conversion de texte en majuscule

Celui-ci, je l'utilise beaucoup, car il gère bien les problèmes d'internationalisation :

awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01    DEC   2018,SONIA,TEAM
52,01    DEC   2018,SONIA,TEAM
25,01    JAN   2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12    JUN   2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST

En fait, c'est probablement la solution la meilleure et la plus portable pour convertir du texte en majuscule à partir du shell.

18. Changer une partie d'une chaîne

Utilisation de la substr commande, vous pouvez diviser une chaîne de caractères à une longueur donnée. Ici, je l'utilise pour mettre en majuscule uniquement le premier caractère du troisième champ :

awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01    dec   2018,Sonia,team
52,01    dec   2018,Sonia,team
25,01    jan   2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12    jun   2018,Öle,team:support
17,05 apr 2019,Abhishek,guest

Le substr La fonction prend la chaîne initiale, l'index (basé sur 1) du premier caractère à extraire et le nombre de caractères à extraire. Si ce dernier argument est manquant, substr prend tous les caractères restants de la chaîne.

Donc, substr($3,1,1) évaluera au premier caractère de $3 , et substr($3,2) aux autres.

19. Fractionner les champs en sous-champs

Le modèle de données de champ d'enregistrement AWK est vraiment sympa. Cependant, vous souhaitez parfois diviser les champs eux-mêmes en plusieurs parties en fonction d'un séparateur interne :

awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019

Assez étonnamment, cela fonctionne même si certains de mes champs sont séparés par plus d'un espace. Principalement pour des raisons historiques, lorsque le séparateur est un espace unique, split considérera "les éléments sont séparés par des séries d'espaces". Et pas seulement par un seul. Le FS variable spéciale suit la même convention.

Cependant, dans le cas général, une chaîne de caractères correspond à un caractère. Donc, si vous avez besoin de quelque chose de plus complexe, vous devez vous rappeler que le séparateur de champs est une expression régulière étendue.

À titre d'exemple, voyons comment serait traité le champ de groupe qui semble être un champ à plusieurs valeurs en utilisant deux-points comme séparateur :

awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest

Alors que je m'attendais à afficher jusqu'à deux groupes par utilisateur, il n'en affiche qu'un pour la plupart d'entre eux. Ce problème est causé par les multiples occurrences du séparateur. Donc, la solution est :

awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest

Les barres obliques au lieu des guillemets indiquent que le littéral est une expression régulière plutôt qu'une chaîne simple, et le signe plus indique que cette expression correspondra à une ou plusieurs occurrences du caractère précédent. Ainsi, dans ce cas, chaque séparateur est constitué (de la séquence la plus longue de) un ou plusieurs deux-points consécutifs.

20. Recherche et remplacement avec des commandes AWK

En parlant d'expressions régulières, parfois vous voulez effectuer une substitution comme le sed s///g commande, mais uniquement sur un champ. Le gsub commande est ce dont vous avez besoin dans ce cas :

awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest

Le gsub La fonction prend une expression régulière à rechercher, une chaîne de remplacement et la variable contenant le texte à modifier en place. Si cette dernière est manquante, 0 $ est supposé.

F. Travailler avec des commandes externes dans AWK

Une autre grande fonctionnalité d'AWK est que vous pouvez facilement invoquer des commandes externes pour traiter vos données. Il y a fondamentalement deux façons de le faire :en utilisant le system instruction pour invoquer un programme et le laisser mélanger sa sortie dans le flux de sortie AWK. Ou en utilisant un tube pour qu'AWK puisse capturer la sortie du programme externe pour un contrôle plus précis du résultat.

Ceux-ci peuvent être des sujets énormes en eux-mêmes, mais voici quelques exemples simples pour vous montrer la puissance derrière ces fonctionnalités.

21. Ajouter la date en haut d'un fichier

awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Dans ce programme AWK, je commence par afficher le travail UPDATED. Ensuite, le programme invoque la date externe commande, qui enverra son résultat sur la sortie juste après le texte produit par AWK à ce stade.
Le reste du programme AWK se contente de supprimer une instruction de mise à jour éventuellement présente dans le fichier et d'imprimer toutes les autres lignes (avec la règle 1 ).

Remarquez le next déclaration. Il est utilisé pour abandonner le traitement de l'enregistrement en cours. C'est un moyen standard d'ignorer certains enregistrements du fichier d'entrée.

22. Modifying a field externally

For more complex cases, you may need to consider the | getline VARIABLE idiom of AWK:

awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01    dec   2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01    dec   2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01    jan   2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12    jun   2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f

This will run the command stored in the CMD variable, read the first line of the output of that command, and store it into the variable $5 .

Pay special attention to the close statement, crucial here as we want AWK to create a new instance of the external command each time it executes the CMD | getline déclaration. Without the close statement, AWK would instead try to read several lines of output from the same command instance.

23. Invoking dynamically generated commands

Commands in AWK are just plain strings without anything special. It is the pipe operator that triggers external programs execution. So, if you need, you can dynamically construct arbitrary complex commands by using the AWK string manipulation functions and operators.

awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"'  FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest

We have already met the printf une fonction. sprintf is very similar but will return the built string rather than sending it to the output.

24. Joining data

To show you the purpose of the close statement, I let you try out that last example:

awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin  1e2a4f52
52 01    dec   2018 sonia team  c23d4b65
52 01    dec   2018 sonia team  347489e5
25 01    jan   2019 sonia team  ba985e55
10 01 jan 2019 sylvain team:::admin  81e9a01c
8 12    jun   2018 öle team:support  4535ba30
17 05 apr 2019 abhishek guest  80a60ec8

As the opposite of the example using the uuid command above, there is here only one instance of od launched while the AWK program is running, and when processing each record, we read one more line of the output of that same process.

Conclusion

That quick tour of AWK certainly can’t replace a full-fledged course or tutorial on that tool. However, for those of you that weren’t familiar with it, I hope it gave you enough ideas so you can immediately add AWK to your toolbox.

On the other hand, if you were already an AWK aficionado, you might have found here some tricks you can use to be more efficient or simply to impress your friends.

However, I do not pretend been exhaustive. So, in all cases, don’t hesitate to share your favorite AWK one-liner or any other AWK tips using the comment section below!


Linux
  1. Premiers pas avec awk, un puissant outil d'analyse de texte

  2. Premiers pas avec Zsh

  3. Premiers pas avec la commande Linux tac

  4. Premiers pas avec la commande Linux cat

  5. Démarrer avec ls

Premiers pas avec les exemples de commandes Alpine Linux Apk

Premiers pas avec l'éditeur de texte Nano [Guide du débutant]

Premiers pas avec Markdown [Guide du débutant]

Premiers pas avec la commande SED [Guide du débutant]

Premiers pas avec Tmux [Guide du débutant]

Premiers pas avec la commande Tar