Ce texte se réfère uniquement à l'implémentation GNU de awk connue sous le nom de gawk Witch est le plus utilisé et est livré avec toute distribution Linux / Unix moderne.
[Le guide de l'utilisateur GNU AWK] [GNU-AWK] est sa référence, pour les exemples que j'ai utilisés des cas réels tirés principalement de mes réponses [Stackoverflow] [donc].
AWK est un langage similaire à Perl, seulement beaucoup plus élégant.
- Arnold Robbins
AWK est un langage de programmation conçu pour le traitement de texte et généralement utilisé comme outil d'extraction et de rapport de données. Il s'agit d'une caractéristique standard de la plupart des systèmes d'exploitation de type Unix.
awk ...Son nom est dérivé des noms de famille de ses auteurs - Alfred A Ho, Peter W einberger et Brian K Ernighan.
awk ...Recherche des lignes qui contiennent certains modèles dans les fichiers ou l'entrée standard.
Principalement utilisés pour l'extraction et les rapports de données, comme le résumé des informations de la sortie d'autres programmes utilitaires.
Syntaxe C-like
Données sur les données: il décrit les données avec lesquelles vous souhaitez travailler, puis quelle action faire lorsque vous les trouvez.
pattern { action }
pattern { action }Si le programme est court :
awk ' program ' input-file1 input-file2Remarque: Méfiez-vous des problèmes de citation de shell 1 .
cmd | awk ' program ' Remarque: Le pipe redirige la sortie de la commande de gauche ( cmd ) vers l' entrée de la commande awk 2 .
Lorsque le code est long , il est généralement plus pratique de le mettre dans un fichier et de l'exécuter avec une commande comme celle-ci:
awk -f program-file input-file1 input-file2
cmd | awk -f program-file Ou faites-le simplement exécutable comme un shebang :
#! /bin/awk -f
BEGIN { print " hello world!! " } -F fs Définissez la variable FS sur fs .
-v var=val Définissez la variable var sur la valeur val avant le début du programme.
Remarque : il peut être utilisé plus d'une fois, en définissant une autre variable à chaque fois.
Ces modèles spéciaux ou blocs fournissent des actions de démarrage et de nettoyage pour les programmes awk .
BEGIN{
// initialize variables
}
{
/pattern/ { action }
}
END{
// cleanup
}BEGIN avant que le premier enregistrement d'entrée ne soit lu, END après la consommation des entrées.
$ echo " hello " | awk ' BEGIN{print "BEGIN";f=1}
{print $f}
END{print "END"} '
BEGIN
hello
ENDgrepping si vous avez awk ?? $ cat lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ grep dolor lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.$ awk ' /dolor/ ' lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.Remarque: Si l'action n'est pas donnée, l' action par défaut consiste à imprimer l'enregistrement qui correspond au modèle donné.
Mais ... comment pouvons-nous découvrir le premier et le dernier mot de chaque ligne?
Bien sûr, grep peut, mais a besoin de deux étapes:
$ grep -Eo ' ^[^ ]+ ' lorem_ipsum.dat
Lorem
Maecenas
Nunc
Curabitur
Lorem
Aliquam$ grep -Eo ' [^ ]+$ ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
ultrices. Voyons awk en action ici:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices. awk divise l'entrée de votre programme en enregistrements et champs .
Les enregistrements sont séparés par un caractère appelé le séparateur d'enregistrement RS . Par défaut , le séparateur d'enregistrement est le caractère Unix Newline n .
C'est pourquoi les enregistrements sont, par défaut , des lignes uniques .
De plus, awk a un séparateur d'enregistrements de sortie ORS pour contrôler la façon dont les enregistrements sont présentés au stdout .
RS et ORS doivent être enfermés en guillemets , qui indiquent une constante de chaîne.
Pour utiliser un caractère différent ou un regex , affectez-le simplement aux variables RS ou / et ORS :
BEGIN , avant que toute entrée ne soit traitée, de sorte que le tout premier enregistrement est lu avec le séparateur approprié.Exemples:
$ awk ' BEGIN{RS=" *, *";ORS="<<<---n"}
{print $0} ' lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- $ awk ' {print $0} ' RS= " *, * " ORS= " <<<---n " lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- Les enregistrements awk sont automatiquement analysés ou séparés en morceaux appelés champs .
Par défaut, les champs sont séparés par des espaces blancs (toute chaîne d'un ou plusieurs espaces, onglets ou newlines), comme les mots dans une ligne.
Pour vous référer à un champ dans un programme awk , vous utilisez un panneau Dollar $ suivi du numéro du champ souhaité.
Ainsi, $1 fait référence au premier champ, $2 à la seconde, etc.
IMPORTANT : $0 représente l'ensemble des enregistrements d'entrée.
$ awk ' {print $3} ' lorem_ipsum.dat
dolor
erat
orci,
dapibus
dolor
mauris NF est une variable prédéfinie. La valeur est le nombre de champs dans l'enregistrement actuel . Ainsi, $NF sera toujours le dernier champ du disque.
$ awk ' {print NF} ' lorem_ipsum.dat
8
7
10
4
8
10contre.
$ awk ' {print $NF} ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
facilisis. FS détient la valeur du séparateur de champs , cette valeur est une chaîne à caractères mono ou un regex qui correspond aux séparations entre les champs dans un enregistrement d'entrée.
La valeur par défaut est " " , une chaîne composée d'un seul espace. En tant qu'exception particulière, cette valeur signifie que toute séquence d' espaces , d'onglets et / ou de lignes NewLines est un seul séparateur.
De la même manière que nous avons une variable OFS pour gérer la façon dont nos champs vont être envoyés au flux ORS sortie.
$ cat /etc/group
nobody: * :-2:
nogroup: * :-1:
wheel: * :0:root
daemon: * :1:root
kmem: * :2:root
sys: * :3:root
tty: * :4:root$ awk ' !/^(_|#)/&&$1=$1 ' FS= " : " OFS= " <-> " /etc/group
nobody < - > * < - > - 2< - >
nogroup < - > * < - > - 1< - >
wheel < - > * < - > 0 < - > root
daemon < - > * < - > 1 < - > root
kmem < - > * < - > 2 < - > root
sys < - > * < - > 3 < - > root
tty < - > * < - > 4 < - > root Remarque : Ummm ... $1=$1 ???? 3
Garder les registres et les champs à l'esprit, étaient maintenant prêts à comprendre notre code précédent:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices.Ce sont deux variables intégrées utiles:
NR : Nombre d'enregistrements d'entrée awk traité depuis le début de l'exécution du programme.
FNR : Numéro d'enregistrement actuel Dans le fichier actuel, awk réinitialise FNR à zéro chaque fois qu'il démarre un nouveau fichier d'entrée.
$ cat n1.dat
one
two$ cat n2.dat
three
four$ awk ' {print NR,FNR,$0} ' n1.dat n2.dat
1 1 one
2 2 two
3 1 three
4 2 fourLa chaîne de format est très similaire à celle de l' iso c .
Syntaxe:
printf format, item1, item2, …
$ awk ' {printf "%20s <-> %sn",$1,$NF} ' lorem_ipsum.dat
Lorem < - > elit.
Maecenas < - > condimentum.
Nunc < - > ex.
Curabitur < - > tellus.
Lorem < - > elit.
Aliquam < - > ultrices. La sortie de print et printf est dirigée vers la sortie standard par défaut, mais nous pouvons utiliser la redirection pour modifier la destination.
Les redirections dans awk sont écrites comme des redirections dans les commandes shell , sauf qu'elles sont écrites dans le programme awk .
$ awk ' BEGIN{print "hello">"hello.dat"} ' $ awk ' BEGIN{print "world!">>"hello.dat"} ' $ cat hello.dat
hello
world !Il est également possible d'envoyer une sortie à un autre programme via un tuyau :
$ awk ' BEGIN{sh="/bin/sh";print "date"|sh;close(sh)} '
dom nov 13 18:36:25 CET 2016 Les [flux] peuvent être pointés vers le stdin , le stdout et le stderr .
Par exemple, nous pouvons écrire un message d'erreur au stderr comme ceci:
$ awk ' BEGIN{print "Serious error detected!" > "/dev/stderr"} '
Serious error detected ! Dans awk, les tableaux sont associatifs , chacun est une collection de paires , index - valeur , où le nombre ou la chaîne peut être un index.
Aucune déclaration n'est nécessaire; De nouvelles paires peuvent être ajoutées à tout moment.
| Indice | Valeur |
|---|---|
| "Perro" | "chien" |
| "Gato" | "chat" |
| "Uno" | "un" |
| 1 | "un" |
| 2 | "deux" |
Pour référer un tableau:
array[index-expression]
Pour attribuer des valeurs:
array[index-expression] = value
Pour vérifier si une clé est indexée:
indx in array
Pour le parcourir:
for (var in array) {
var, array[var]
}Utilisation des valeurs numériques comme index et préservant l'ordre:
for (i = 1 ; i < = max_index ; i++) {
print array[i]
}Un exemple complet:
$ cat dict.dat
uno one
dos two
tres three
cuatro fourawk ' {dict[$1]=$2}
END{if ("uno" in dict)
print "Yes we have uno in dict!"
if (!("cinco" in dict))
print "No , cinco is not in dict!"
for (esp in dict){
print esp, "->" ,dict[esp]
}
} ' dict.datVous donne:
Yes we have uno in dict !
No , cinco is not in dict !
uno - > one
dos - > two
tres - > three
cuatro - > four gawk ne trie pas les tableaux par défaut:
awk ' BEGIN{
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 4 four
0 zero
1 one
2 two
3 threeMais vous pouvez profiter de [Procinfo] pour le tri:
awk ' BEGIN{
PROCINFO["sorted_in"] = "@ind_num_asc"
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 0 zero
1 one
2 two
3 three
4 four gensub(regexp, replacement, how [, target]) : est la fonction la plus avancée pour le remplacement de la chaîne.
Et leurs alternatives plus simples:
gsub(regexp, replacement [, target])
sub(regexp, replacement [, target])
Avoir ce fichier:
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.Nous allons échanger la position des mots placés à gauche et à droite de chaque virgule.
$ awk ' {print gensub(/([^ ]+)( *, *)([^ ]+)/,
"\3\2\1", "g")} ' lorem.dat
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim euismod, orci id nisi interdum, eget cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Aliquam interdum mauris volutpat nisl et, placerat facilisis. En utilisant gensub nous captions trois groupes , puis nous échangeons l'ordre.
Pour illustrer une action plus simple, changeons de points pour les virgules :
awk ' $0=gensub(/./, ",", "g") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis, Utilisation de l'alternative gsub :
awk ' gsub(/./, ",") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis,Cette option semble meilleure lorsqu'aucune capture de groupe n'est nécessaire.
D'autres fonctions intéressantes sont index et substr .
index(in, find)
substr(string, start [, length ])
Fonctionne comme ceci:
$ awk ' BEGIN{t="hello-world";print index(t, "-")} '
6$ awk ' BEGIN{t="hello-world";print substr(t,index(t, "-")+1)} '
world La fonction split est utilisée pour créer un tableau à partir d'une chaîne le divisant par un charbon séparateur , il renvoie le nombre d'éléments du tableau créé.
split(string, array [, fieldsep [, seps ] ])
$ cat passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbash$ awk ' n=split($0, a, ":"){print n, a[n]} ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashRemarque : cela pourrait être fait d'une manière beaucoup plus simple:
$ awk ' {print NF,$NF} ' FS= ' : ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashÉcrire une fonction personnalisée est assez simple:
awk ' function test(m)
{
printf "This is a test func, parameter: %sn", m
}
BEGIN{test("param")} 'Donnez-nous:
This is a test func, parameter: param Nous pouvons également redonner une expression en utilisant une instruction return :
awk ' function test(m)
{
return sprintf("This is a test func, parameter: %s", m)
}
BEGIN{print test("param")} 'L'analyse par paramètre est le seul moyen de faire une variable locale à l'intérieur d'une fonction.
Les valeurs scalaires sont transmises par valeur et des tableaux par référence, donc toute modification apportée à un tableau à l'intérieur d'une fonction sera reflétée dans la portée globale:
awk ' function test(m)
{
m[0] = "new"
}
BEGIN{m[0]=1
test(m)
exit
}
END{print m[0]} 'Sorties:
newNos défis :
01. Avant-dernier mot d'un dossier.
02. Remplacement d'un enregistrement.
03. Placez un point-virgule à la fin de chaque enregistrement.
04. Placez une virgule entre chaque mot.
05. Tous ensemble?
06. Rediriger les enregistrements impairs vers un fichier et même ceux vers un autre.
07. Étant donné un fichier de mot de passe, obtenez le champ manquant.
08. Échange de champ.
09. piratage traceroute.
10. Où sont mes enfants?
11. Aggrégation de données.
12. Enregistrements entre deux modèles.
13. Transformation de champ.
14. Records aux colonnes.
15. Traitement de fichiers FASTA.
16. Représentation complexe.
17. Files Joiner.
18. Passwd et groupe.
19. Connexions utilisateur.
20. Moyenne de charge totale du temps de disponibilité.
Avoir ce fichier source :
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' {print $(NF-1)} ' lorem.dat
adipiscing
consectetur
cursus
dapibus
adipiscing
neque Pas trop à expliquer ici, NF stocke le nombre de champs dans l'enregistrement actuel, donc NF-1 pointe vers le champ avant Last et $(NF-1) sera sa valeur.
Notre tâche, la substitution des enregistrements de fichiers, la troisième ligne doit devenir:
This not latin
Rien de plus simple, il suffit de jouer autour de NR ( nombre d'enregistrements ).
Code:
$ awk ' NR==3{print "This is not latin";next}{print} ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. Solution alternative pour éviter la déclaration next : attribuez la nouvelle ligne à l'enregistrement complet $0 .
Exemple:
$ awk ' NR==3{$0="This is not latin"}1 ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' 1 ' ORS= " ;n " lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Maecenas pellentesque erat vel tortor consectetur condimentum. ;
Nunc enim orci, euismod id nisi eget, interdum cursus ex. ;
Curabitur a dapibus tellus. ;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Aliquam interdum mauris volutpat nisl placerat, et facilisis neque ultrices. ; Comme le RS par défaut est la ligne de rupture Unix n nous avons juste besoin de préfixer le point-virgule au OFS d'enregistrement de sortie.
1 ? 4
$ awk ' {$1=$1}1 ' OFS= ' , ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum.
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex.
Curabitur,a,dapibus,tellus.
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. La partie la plus importante de ce code est la façon dont elle oblige une reconstruction record avec $1=$1 pour la valeur actuelle de l' OFS .
$ awk ' {$1=$1}1 ' OFS= ' , ' ORS= ' ;n ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum. ;
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex. ;
Curabitur,a,dapibus,tellus. ;
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. ; Comme simplement jouer avec des Vars de sortie: OFS et ORS .
Commençons par la solution finale:
$ awk ' NR%2{print > "even.dat";next}
{print > "odd.dat"} ' lorem.dat$ cat even.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Lorem ipsum dolor sit amet, consectetur adipiscing elit$ cat odd.dat
Maecenas pellentesque erat vel tortor consectetur condimentum.
Curabitur a dapibus tellus.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. La fonction [modulo] ( % ) trouve le reste après la division pour le numéro d'enregistrement actuel NR divisé par deux:
$ awk ' {print NR%2} ' lorem.dat
1
0
1
0
1
0 Pour autant que nous maintenant, dans awk 1 est vrai et 0 faux . Nous redirigeons notre sortie en évaluant ce fait.
next il faut une attention particulière, il oblige awk à arrêter immédiatement le processus d'enregistrement actuel et à passer à la suivante.
De cette façon, nous échappons à une double condition qui ressemblerait à ceci:
awk ' NR % 2{print > "even.dat"}
!NR % 2{print > "odd.dat"} ' lorem.dat$ cat /etc/passwd
jd001:x:1032:666:Javier Diaz::/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez::/bin/rbash
jp003:x:1000:666:Jose Perez::/bin/bash
ms004:x:8051:668:Maria Saenz::/bin/rbash
rc005:x:6550:668:Rosa Camacho::/bin/rbash Supposons le répertoire domestique en préfixant la chaîne fixe "/home/" au nom d'utilisateur :
$ awk ' $6="/home/"$1 ' FS= ' : ' OFS= ' : ' /etc/passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbashNotre première étape devrait être considérée comme le séparateur de champ, un côlon, pour l'entrée ainsi que pour la sortie.
Ensuite, nous devons trouver la position du champ vide, 6 pour cet exemple.
Enfin, nous composons la valeur requise en utilisant la chaîne donnée et la connexion de l'utilisateur stockée dans le premier champ.
print n'est pas nécessaire car la valeur de retour d'attribution $6 sera toujours vraie et l'action par défaut awk est d'imprimer l'enregistrement affecté.
Notre objectif: le dernier champ devrait devenir le premier et le premier de devenir le dernier.
Code final:
$ awk -F : ' {last=$1;$1=$NF;$NF=last}1 ' FS= " : " OFS= ' : ' /etc/passwd
/bin/rbash:x:1032:666:Javier Diaz:/home/jd001:jd001
/bin/rbash:x:8050:668:Alejandro Gonzalez:/home/ag002:ag002
/bin/bash:x:1000:666:Jose Perez:/home/jp003:jp003
/bin/rbash:x:8051:668:Maria Saenz:/home/ms004:ms004
/bin/rbash:x:6550:668:Rosa Camacho:/home/rc005:rc005 Nous jouons avec une variable intermédiaire utilisée pour stocker la première valeur de champ, nous échangeons sa valeur avec le dernier, enfin nous attribuons last variable à $NF ( $NF=last ).
Avoir cette sortie:
$ traceroute -q 1 google.com 2> /dev/null
1 hitronhub.home (192.168.1.1) 5.578 ms
2 217.217.0.1.dyn.user.ono.com (217.217.0.1) 9.732 ms
3 10.127.54.181 (10.127.54.181) 10.198 ms
4 62.42.228.62.static.user.ono.com (62.42.228.62) 35.519 ms
5 72.14.235.20 (72.14.235.20) 26.003 ms
6 216.239.50.133 (216.239.50.133) 25.678 ms
7 mad01s24-in-f14.1e100.net (216.58.211.238) 25.019 msNous devons calculer le temps total de voyage .
$ traceroute -q 1 google.com 2> /dev/null |
awk ' {total+=$(NF-1)}
END{print "Total ms: "total} '
Total ms: 153.424Étant donné qu'aucune condition n'est spécifiée, l'action est exécutée pour tous les enregistrements .
total+=$(NF-1) : la variable total est utilisée pour accumuler la valeur de chaque avant-dernier champ $ $(NF-1) .
Enfin, nous utilisons la règle END pour afficher la valeur total finale.
Notre travail: obtenez nos processus dépendants shell .
$ echo $$
51026Première chose : lancez les processus d'arrière-plan.
$ sleep 10 & sleep 15 & sleep 20 &
[1] 86751
[2] 86752
[3] 86753 En utilisant ps Utility , awk recherchera le troisième champ connu sous le nom de PPID .
Remarque : nous utilisons -v pour définir PPID VAR avant le début de l'exécution du programme.
$ ps -ef | awk -v ppid= $$ ' $3==ppid '
501 86751 51026 0 7:57PM ttys001 0:00.00 sleep 10
501 86752 51026 0 7:57PM ttys001 0:00.00 sleep 15
501 86753 51026 0 7:57PM ttys001 0:00.00 sleep 20
0 86754 51026 0 7:57PM ttys001 0:00.00 ps -ef
501 86755 51026 0 7:57PM ttys001 0:00.00 awk $3 ==51026Nous avons juste besoin de dormir :
$ ps -ef | awk -v ppid= $$ ' $3 == ppid && /slee[p]/
{print $2" -> "$5} '
86751 - > 7:57PM
86752 - > 7:57PM
86753 - > 7:57PM La solution a besoin d'une nouvelle condition pour ajouter: trouver le modèle de sommeil dans notre enregistrement /slee[p]/ .
L'action déclenchée sera d'imprimer le deuxième champ $2 avec des supports pour le PID et le cinquième $5 , l'horodatage .
Avoir ce fichier:
$ cat ips.dat
IP BYTES
81.220.49.127 328
81.220.49.127 328
81.220.49.127 329
81.220.49.127 367
81.220.49.127 5302
81.226.10.238 328
81.227.128.93 84700 Notre tâche consiste à calculer le nombre d' octets par IP traités.
$ awk ' NR>1{ips[$1]+=$2}
END{for (ip in ips){print ip, ips[ip]}} ' ips.dat
81.220.49.127 6654
81.227.128.93 84700
81.226.10.238 328Bouquet de choses ici pour expliquer.
NR>1{ips[$1]+=$2} : l'action ips[$1]+=$2 n'est exécutée que lorsque le numéro d'enregistrement actuel est supérieur à un NR>1 . Ceci est nécessaire pour éviter l'en-tête.
ips est un tableau indexé par la valeur IP (le champ $1 ), pour chaque clé que nous allons accumuler dans la valeur du deuxième champ.
Prenez note d'un fait important , si une clé n'est pas présente dans le tableau , awk ajoute un nouvel élément à la structure , sinon va mettre à jour la valeur précédente pointée par cette clé (comme dans notre exemple).
La règle END est juste utilisée pour itérer le tableau par des index et des valeurs.
Ce code pourrait être réécrit de manière complète différente pour éviter l'utilisation des tableaux et préserver l'ordre tiré du fichier IPS trié:
awk ' NR==1{next}
lip && lip != $1{print lip,sum;sum=0}
{sum+=$2;lip=$1}
END{print lip,sum} ' ips.dat
81.220.49.127 6654
81.226.10.238 328
81.227.128.93 84700 NR==1{next} : contourner l'en-tête.
lip && lip != $1{print lip,sum;sum=0} : ici, nous utilisons un var nommé lip ( Last-Ip ). lip && lip != $1 Lorsque lip n'est pas nul et sa valeur n'est pas égale au premier champ (qui maintient l'IP actuel) L'action déclenchée sera d'imprimer lip et sum la quantité totale d'octets pour la dernière IP. Ensuite, nous l'initialisons sum=0 .
L' astuce est claire, chaque fois que IP ( $1 ) modifie, nous montrons les statistiques de la précédente.
{sum+=$2;lip=$1} : pour mettre à jour le compteur d'octets sum+=$2 et attribuer l' IP actuel à lip : lip=$1 . Il s'agit de la dernière étape de notre traitement des enregistrements.
Le bloc END est utilisé pour imprimer les valeurs en attente .
Ce code préserve l'ordre, mais à mon avis , cela se fait au détriment d'une complexité considérablement accrue.
Notre tâche est deux extraire les lignes entre et OUTPUT et END .
$ cat pat.dat
test -3
test -2
test -1
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
END
test 1
test 2
test 3 Ceci est un exemple classique utilisé pour illustrer comment la correspondance de motifs fonctionne dans awk et ses actions associées que j'ai dédié un [post] complet.
$ awk ' /END/{flag=0}flag;/OUTPUT/{flag=1} ' pat.dat
top 2
bottom 1
left 0
right 0
page 66 Il est basé dans la valeur de la variable flag , il sera vrai ( 1 ) lorsque la OUTPUT du modèle de démarrage est trouvée et fausse ( 0 ) lorsque la balise END est atteinte.
Pour éviter une étape supplémentaire, l'ordre d'action est très important , si nous suivons la séquence logique :
$ awk ' /OUTPUT/{flag=1}flag;/END/{flag=0} ' pat.dat
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
ENDLes balises de motif sont affichées à travers la sortie.
La raison: une fois que le modèle OUTPUT est trouvé, l' indicateur est activé, car la prochaine action dépend de ce drapeau, l'enregistrement est imprimé.
Nous pouvons éviter ce comportement en plaçant l'activation du drapeau comme la dernière étape du flux.
Supposons ce fichier:
$ cat space.dat
10.80 kb
60.08 kb
35.40 kb
2.20 MB
1.10 MB
40.80 kb
3.15 MB
20.50 kbNotre travail consistera à calculer nos enregistrements de poids total dans les méga octets :
$ awk ' {total+= $1 / ($2=="kb" ? 1024: 1)}
END{print total} ' space.dat
6.61365Pour comprendre comment cela fonctionne, un concept doit être clair, l' opérateur [ternaire] (sujet d'un ancien poste).
total sera utilisé pour accumuler la division du premier champ $1 par les 2 $ des $2 qui conserveront la valeur donnée par l' opérateur ternaire : 1024 lorsque $2 est égal à kb et 1 si aucune transformation ne nécessaire.
Enfin, nous imprimons la valeur total dans le bloc END .
Source d'origine:
$ cat group.dat
string1
string2
string3
string4
string5
string6
string8Notre mission est de regrouper les enregistrements en blocs de trois colonnes comme celle-ci:
string1 string2 string3
string4 string5 string6
string8 Cela peut sembler complexe, mais devient beaucoup plus simple si nous OFS comment utiliser le séparateur de champ de sortie :
$ awk ' ORS = NR%3 ? FS : RS; END{print "n"} ' group.dat
string1 string2 string3
string4 string5 string6
string8 Si nous définissons l' ORS sur un caractère vide , la valeur par défaut FS , toute la sortie deviendra une seule ligne:
$ awk ' ORS=FS; END{print "n"} ' group.dat
string1 string2 string3 string4 string5 string6 string7 ORS = NR%3 ? FS : RS : Enfin, nous utilisons l' opérateur ternaire (expliqué juste avant) pour évaluer le [modulo] NR%3 Résultat de la division du numéro de champ actuel NR par trois.
Si le reste est vrai , l' ORS devient FS , un espace vide , sinon la valeur par défaut RS sera attribuée, la ligne Unix Break n .
En bioinformatique , [Fasta] est un format de fichier textuel.
Avoir l'exemple suivant:
$ cat fasta.dat
> header1
CGCTCTCTCCATCTCTCTACCCTCTCCCTCTCTCTCGGATAGCTAGCTCTTCTTCCTCCT
TCCTCCGTTTGGATCAGACGAGAGGGTATGTAGTGGTGCACCACGAGTTGGTGAAGC
> header2
GGT
> header3
TTATGATNous avons besoin de la longueur totale de chaque séquence et d'un curriculum vitae final .
Devrait ressembler à ceci:
> header1
117
> header2
3
> header3
7
3 sequences, total length 127 awk est l'outil parfait pour cet effort de rapport , pour cet exemple que nous utiliserons:
awk ' /^>/ { if (seqlen) {
print seqlen
}
print
seqtotal+=seqlen
seqlen=0
seq+=1
next
}
{
seqlen += length($0)
}
END{print seqlen
print seq" sequences, total length " seqtotal+seqlen
} ' fasta.dat
La première action est liée à la détection de l'en-tête /^>/ , c'est parce que tous les en-têtes jouent avec > caractère.
Lorsque seqlen n'est pas nul sa valeur, qui maintient la longueur de séquence précédente, est imprimé au stdout attaché au nouvel en-tête. seqtotal est mis à jour et seqlen initialisé pour servir la séquence suivante. Enfin, nous rompons davantage le traitement des enregistrements avec next .
La deuxième action {seqlen += length($0)} est utilisée pour mettre à jour seqlen résumant la longueur totale d'enregistrement.
Le but de la règle END est de montrer la séquence non imprimée et les totaux.
L'astuce ici est d'imprimer la longueur de séquence précédente lorsque nous avons trouvé un nouvel en-tête .
Lorsque nous traitons le premier enregistrement, seqlen n'a aucune valeur, nous ignorons donc la visualisation.
Source:
$ cat report.dat
snaps1: Counter: 4966
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 0
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.test.attributes
snaps1: Counter: 5660
Opens: Counter: 37283
Instance: s.1.aps.userDatabase.mount275000.attributes Notre devoir : Créez un rapport pour visualiser snaps et instance , mais uniquement lorsque la première balise SNAP First Counter est supérieure à zéro .
Sortie attendue:
snaps1: Counter: 4966
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 5660
Instance: s.1.aps.userDatabase.mount275000.attributesNous jouons à nouveau autour des modèles et des drapeaux :
awk ' {$1=$1}
/snaps1/ && $NF>0{print;f=1}
f && /Instance/ {print;f=0} ' report.dat Pour chaque enregistrement , la première action est exécutée , il oblige awk à reconstruire l'enregistrement entier , en utilisant les valeurs actuelles pour OFS 3 .
Cette astuce nous permet de convertir un séparateur d'espace multiple en un seul char , la valeur par défaut pour le séparateur de champ de sortie.
Voyons ceci:
$ awk ' 1 ' text.dat
one two
three four$ awk ' $1=$1 ' text.dat
one two
three four La deuxième action est déclenchée lorsque le modèle est trouvé et le dernier champ supérieur à zéro /snaps1/ && $NF>0 .
awk imprime l'enregistrement et attribuer une vraie valeur à l' print;f=1 .
Dernière étape: lorsque l'indicateur est vrai et le modèle d'instance dans la ligne f && /Instance/ , affichez la ligne et désactivez l'indicateur: print;f=0 .
Supposons deux archives:
$ cat join1.dat
3.5 22
5. 23
4.2 42
4.5 44$ cat join2.dat
3.5
3.7
5.
6.5 Nous avons besoin des enregistrements du premier join1.dat lorsque les premiers champs sont dans le deuxième join2.dat .
La sortie doit être:
3.5 22
5. 23Nous pouvons utiliser l'utilitaire UNIX Join , bien sûr, mais nous devons trier le premier fichier:
$ join <( sort join1.dat ) join2.dat
3.5 22
5. 23 Pas nécessaire dans awk :
$ awk ' NR == FNR{a[$1];next}
$1 in a ' join2.dat join1.datÉtudions les filtres et les actions:
NR == FNR : numéro d'enregistrement égal au numéro de fichier d'enregistrement signifie que nous traitons le premier fichier analysé à awk : join2.dat .
L'action de la paire a[$1];next sera d'ajouter une nouvelle valeur vide au tableau indexé par le premier champ. next instruction rompra le traitement des enregistrements et passera le flux au suivant .
Pour la deuxième action, NR != FNR est appliqué implicitement et n'affecte que pour join1.dat , la deuxième condition est $1 in a qui sera vrai lorsque le premier champ de join1.dat est une clé de tableau.
Ce sont des classiques Unix :
$ cat /etc/group
dba:x:001:
netadmin:x:002:$ cat /etc/passwd
jd001:x:1032:001:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:002:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:001:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:002:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:002:Rosa Camacho:/home/rc005:/bin/rbashNotre objectif, un rapport comme celui-ci:
d001:dba
ag002:netadmin
jp003:dba
ms004:netadmin
rc005:netadminNous avons besoin d'un flux de fichiers multiples comme nous avons étudié dans notre dernier exemple:
$ awk -F : ' NR == FNR{g[$3]=$1;next}
$4 in g{print $1""FS""g[$4]} ' /etc/group /etc/passwd Pour traiter /etc/group nous répétons la comparaison NR == FNR , puis stockez le nom du groupe $1 indexé par son identifiant $3 : g[$3]=$1 . Enfin, nous rompons davantage le traitement des enregistrements avec next .
La deuxième condition $4 in g uniquement $4 /etc/passwd g[$4] print $1""FS""g[$4]
Exemple de sortie de l'utilitaire des utilisateurs:
$ users
negan rick bart klashxx klashxx ironman ironman ironmanNous allons compter les connexions par utilisateur.
$ users | awk ' {a[$1]++}
END{for (i in a){print i,a[i]}} ' RS= ' + '
rick 1
bart 1
ironman 3
negan 1
klashxx 2L'action est effectuée pour tous les enregistrements.
a[$1]++ : il s'agit du compteur , pour chaque utilisateur $1 il incrémente la valeur pointue (Vars non initialisée a la valeur numérique zéro).
Dans le bloc END itérez le tableau par clé et la valeur stockée pour présenter les résultats.
Une sortie typique :
$ uptime
11:08:51 up 121 days, 13:09, 10 users, load average: 9.12, 14.85, 20.84Comment pouvons-nous obtenir la moyenne moyenne de charge totale?
$ uptime | awk ' {printf "Load average mean: %0.2fn",
($(NF-2)+$(NF-1)+$(NF))/3 } ' FS= ' (:|,) + '
Load average mean: 14.94Voici une nouvelle technique.
Nous utilisons un [regex] comme séparateur de champ (:|,) + , de sorte que le FS peut être un côlon et une virgule suivi de zéro ou plus d'espaces vides.
Nous avons juste besoin des trois derniers champs pour effectuer l'arithmétique requise, puis nous utilisons printf attaché à un masque approprié .
Si vous êtes toujours là, merci !!
De mon point de vue, awk est un langage sous-estimé et a besoin de beaucoup d'amour ❤️.
Si vous avez faim de plus, faites-le moi savoir dans la section des commentaires Bellow et que je considérerai une deuxième partie pour terminer ma mission ... vous a permis à mort.
Codage heureux!
[Un guide de la citation de shell Unix] [Citing-Guide]. ↩
[Wikipedia sur les pipelines] [Pipes]. ↩
Il y a des moments où il est pratique de forcer awk à reconstruire l'ensemble de l'enregistrement, en utilisant les valeurs actuelles du FS et OFS .
Pour ce faire, nous utilisons la mission apparemment inoffensive: $1 = $1 ↩ ↩ 2
Réponse rapide, c'est juste un raccourci pour éviter d'utiliser l'instruction d'impression.
Dans awk lorsqu'une condition correspond, l' action par défaut est d'imprimer la ligne d'entrée.
$ echo "test" |awk '1'
Équivaut à:
echo "test"|awk '1==1'
echo "test"|awk '{if (1==1){print}}'
C'est parce que 1 sera toujours [vrai]. ↩