doku:αΩ « Wikidd »

des 𝞹-pes, du sudo maso & la main sur l'hacker !

User Tools

Site Tools


prog:awk

GNU Awk

À propos

Un interpréteur qu'il est chouette pour bidouiller. Concrètement, il s'agit d'un language d'analyse et de traitement de chaîne de caractères (pattern scanning and processing language). S'il n'est pas déjà installé sur votre système, il vous reste un espoir : $ sudo apt-get install awk ou votre variante qui va bien.

On peut déclarer l'utilisation de l'interpréteur au début de chaque script : #!/usr/bin/awk (le chemin qui pointe vers la commande), suivi d'éventuelles options notamment -f:

       -f program-file
       --file program-file
Read  the AWK program source from the file program-file, instead of from the first command line argument.

Multiple -f (or --file) options may be used.

Le principe général du programme est de lire ce qu'il reçoit en entrée, ligne par ligne, reconnaître les motifs qu'on lui donnent et les traiter à partir d'instructions logiques. Comme dans toute épopée, il y a toujours trois actes1) :

BEGIN {}  # le début, avant de commencer à lire
{}        # la lecture, analyse, traitement
END {}    # la fin, après la lecture

Chaque blocs peut contenir ou non des instructions. Par exemple, pour compter les lignes lues et l'afficher à la fin (dans un script count_lines) :

#/usr/bin/awk -f
BEGIN {}
{ nlines++ }
END { print "count:",nlines }
$ ls -l | count_lines
count: 8

Dans le bloc de lecture, chaque ligne correspond à l'argument $0 et chaque termes de la ligne (en fonction de séparateur utilisé) est incrémenté à partir de $1. Si on veut voir chaque ligne :

#/usr/bin/awk -f
BEGIN {}
{ nlines++ ; print $0 }
END { print "count:",nlines }
$ ls -l | count_lines 
total 44
drwxr-xr-x 2 duke duke  4096 janv. 14 18:25 blade_runner
drwxr-xr-x 2 duke duke  4096 janv. 14 18:25 digital_7
-rw-r--r-- 1 duke duke 19164 janv. 14 18:25 game_over.ttf
drwxr-xr-x 2 duke duke  4096 janv. 14 18:25 nasalization
drwxr-xr-x 2 duke duke  4096 janv. 14 18:25 squarefont
drwxr-xr-x 2 duke duke  4096 janv. 14 18:25 squares2
drwxr-xr-x 2 duke duke  4096 janv. 14 18:25 taurus_mono
count: 8

Si on ne veut qu'un seul argument :

#/usr/bin/awk -f
BEGIN {}
{ nlines++ ; print $1 }
END { print "count:",nlines }
$ ls -l | count_lines 
total
drwxr-xr-x
drwxr-xr-x
-rw-r--r--
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
count: 8

Exemples

Changer le séparateur

$ echo "v1, v2,v3" | awk 'BEGIN { FS = "," } ; { print $2; print $1 }'
 v2
v1

Compter les fichiers des utilisateurs

À partir de l'emplacement de travail (ie, pwd), on veut savoir combien de fichiers appartiennent aux utilisateurs du système :

$ ls -l
total 64
drwxr-xr-x  4 duke duke 4096 janv. 28 05:37 0_safe_harbor
-rw-r--r--  1 root root 4830 janv. 25 22:52 col_code
[...]

On voit que la colonne qui nous intéresse est la troisième (et quatrième pour le groupe), on va donc compter les occurrences de chaque valeurs rencontrées dans la troisième colonne dans un script filter_user :

#!/usr/bin/awk -f
BEGIN { print "USER COUNT" }
{ if ($3>"") { username[$3]++; } }
END { for (i in username) { print i,username[i]; } }

Le script est minimaliste est exploite le fait que awk utilise des tableaux associatifs que l'on peut voir comme des dictionnaires (éventuellement multidimensionnels) : l'index est toujours une chaîne de caractères (ici l'argument (ou colonne) 3 pour chaque ligne où le champ n'est pas vide).

Le filtre tel quel permet de sonder les chemins listés :

$ ls -l | filter_user
USER COUNT
root 4
duke 10

Si vous trouvez ça pratique, vous pouvez en faire un alias :

$ alias ucount='ls -l | filter_user'
$ ucount
USER COUNT
root 4
duke 10

Filtrer des logs

Ce script filter_log filtre mes logs pour Apache :

#!/usr/bin/awk -f
BEGIN {}
{
	gsub(/[[]|[]]/,"")
	if ($1 ~ /[0-9]+\/[A-Za-z]+\/[0-9]{,4}/) {
		if (NR==1) { time0=$1 }
		else       { time1=$1 }
		sub($1,"\033[2;36m"$1"\033[0m")
		if ($3 !~ /\-/) sub($3,"\033[2;32m"$3"\033[0m")
		if ($6 !~ /\-/) sub($6,"\033[1;37m"$6"\033[0m")
		if ($8 !~ /\-/) sub($8,"\033[2;36m"$8"\033[0m")
		print $0
		nlines++
	} else { sub($0,"\033[0;38m"$0"\033[0m") }
}
END {
	printf("%-10s %-5d\n","LINES",nlines)
	split(time0,a0,"/")
	split(a0[3],hms0,":")
	split(time1,a1,"/")
	split(a1[3],hms1,":")
	h0 = hms0[2] ; m0 = hms0[3] ; s0 = hms0[4]
	h1 = hms1[2] ; m1 = hms1[3] ; s1 = hms1[4]
	t0 = h0*3600+m0*60+s0
	t1 = h1*3600+m1*60+s1
	ds = t1-t0
	if (ds<0) { ds=-ds }
	dd = int(a1[1])-int(a0[1]) ; dh = int(ds/3600)
	dm = int((ds%3600)/60) ; ds = ds-dm*60-dh*3600
	printf("%-10s %d:%d:%d\n","TIME",dh+dd*24,dm,ds)
}

Pour le log suivant :

$ tail -n4 allaccess.log 
[29/Jan/2017:10:28:28 +0100] POST /tt-rss/backend.php HTTP/1.1 192.168.0.3-192.168.0.3-443 + 200 827 0 - Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0
[29/Jan/2017:10:28:37 +0100] GET /shaarli/?do=atom&searchtags=9gag HTTP/1.1 163.172.66.81-163-172-66-81.rev.poneytelecom.eu-443 + 200 4813 0 - Mozilla/5.0 (compatible; AhrefsBot/5.2; +http://ahrefs.com/robot/)
[29/Jan/2017:10:29:28 +0100] POST /tt-rss/backend.php HTTP/1.1 192.168.0.3-192.168.0.3-443 + 200 2923 0 - Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0
[29/Jan/2017:10:29:46 +0100] GET /shaarli/?removetag=games HTTP/1.1 163.172.66.75-163-172-66-75.rev.poneytelecom.eu-443 + 302 3617 0 - Mozilla/5.0 (compatible; AhrefsBot/5.2; +http://ahrefs.com/robot/)

Le filtre compte les lignes et la durée entre la première et dernière ligne :

$ tail -n4 allaccess.log | filter_log
29/Jan/2017:10:28:28 +0100 POST /tt-rss/backend.php HTTP/1.1 192.168.0.3-192.168.0.3-443 + 200 827 0 - Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0
29/Jan/2017:10:28:37 +0100 GET /shaarli/?do=atom&searchtags=9gag HTTP/1.1 163.172.66.81-163-172-66-81.rev.poneytelecom.eu-443 + 200 4813 0 - Mozilla/5.0 (compatible; AhrefsBot/5.2; +http://ahrefs.com/robot/)
29/Jan/2017:10:29:28 +0100 POST /tt-rss/backend.php HTTP/1.1 192.168.0.3-192.168.0.3-443 + 200 2923 0 - Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0
29/Jan/2017:10:29:46 +0100 GET /shaarli/?removetag=games HTTP/1.1 163.172.66.75-163-172-66-75.rev.poneytelecom.eu-443 + 302 3617 0 - Mozilla/5.0 (compatible; AhrefsBot/5.2; +http://ahrefs.com/robot/)
LINES      4    
TIME       0:1:18

Références

1)
un début, un milieu et une fin
/home/duke/www/dukeart/wiki/data/pages/prog/awk.txt · Last modified: 2020/12/18 21:49 (external edit)