awk e linea di comando

La shell di UNIX/Linux è uno strumento potentissimo che permette di velocizzare ed automatizzare le più svariate operazione. Dato che in UNIX ogni cosa è un file, sono di fondamentale importanza quegli strumenti che permettono di analizzare e gestire i file.

Spessissimo i file con cui si ha a che fare nelle attività sistemistiche sono dei rudimentali database strutturati con i record in righe e i campi in colonne. In questa categoria rientrano ad esempio i file passwd e moltissimi file di log.

Per poter automatizzare operazioni su questo tipo di file è necessario uno strumento che dia accesso a specifici campi nei vari record. Come sempre in ambiente UNIX/Linux è possibile procedere in diversi modi ma uno strumento di sicuoro interesse è awk. Si tratta di uno di quei tool la cui conoscenza non dovrebbe mancare a nessun sistemista.

Nella sua forma più semplice awk accetta in stdin un file, e permette di estrarre dei campi da ogni riga.

Ed esempio se volessi estrarre gli utenti che hanno script schedulati nel seguente file crontab:

17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

potrei utilizzare la seguente righa di comando:

cat /etc/crontab | awk '{print $6}'

all’interno di awk ogni campo, infatti, è contenuto in una variabile $n, dove n è il numero del campo ed i campi sono separati de default da spazi. L’intera riga è contenuta nella variabile $0. Ci sono molte altre variabili predefinite in awk e la lista completa è riportata nel suo manuale (man awk). Una menzione la merita $NR che contiene il numero della riga che si sta elaborando.

Non in tutti i file il separatore è lo spazio; l’opzione -F permette di scegliere un separatore differente.

awk può essere utilizzato come filtro sfruttando il costrutto if al suo interno:

Ad esempio se voglio sapere quali utenti non hanno una shell reale posso utilizzare:

cat /etc/passwd|awk -F: '{if ($7=="/bin/false") print $1}'

Di grande interesse anche i costrutti BEGIN ed END che permettono di definire blocchi di operazioni da eseguire prima o dopo l’esame del file. Posso ad esempio riscrivere lo stesso esempio di prima come:

cat /etc/passwd|awk 'BEGIN{FS=":"}{if ($7=="/bin/false") print $1}'

awk è un vero e proprio linguaggio di programmazione anche se abbastanza semplice, e le possibilità che offre sono molto ampie; nondimeno la sua utilità è soprattutto nelle sue forme più semplici dove, con molta semplicità, permette di improvvisare filtri o operazioni anche molto sofisticati.

Concludo con un esempio dove mostro come awk può essere utilizzato per calcolare la durata media delle slow query a partire da un file di log di mysql:

cat mysql-slow.log |grep Query_time|awk '{cont++;totale+=$3}END{media=totale/cont; print "media = " media}'

perl e linea di comando

Il perl con le sue espressioni regolari è anche un fantastico tool per la linea di comando in grado di sostituire e surclassare i pur notevoli awk e sed. Usato con le opzioni del caso, infatti, il perl ne riprende tutte le caratteristiche ma aggiunge la sua flessibilità e ricchezza.

Le opzioni chiave per l’uso da linea di comando sono “-p” e “-n“: in entrambi i casi lo script o il comando in perl viene eseguito per ogni riga del file in ingresso ma nel secondo caso il risultato va in output.

Queste opzioni corrispondono ai loop che riporto sotto:

-n

LINE:
while (<>) {
        ...             # programma
      }

-p

LINE:
while (<>) {
         ...             # programma
      } continue {
         print or die "-p destination: $!\n";
      }

Molto utili sono anche

  • -e che permette di inserire direttamente sulla linea di comando il codice perl da eseguire
  • -i che fa si che venga modificato direttamente il file che viene letto

Ad esempio entrambi i seguenti comandi

cat /etc/passwd |perl -pe 's/(.*):(.*):(.*):(.*):(.*):(.*):(.*)/$1\t\t$6/'
cat /etc/passwd | awk -F: '{print $1"\t\t"$6}'

permettono di estrarre dal file password gli utenti e le loro home.

Perl, utilizzato in questo modo, permette di definire dei blocchi di codice BEGIN e END per eventuali operazioni preliminari a o conclusive del loop implicito: si immagini il caso in cui si voglia contare il numero di occorrenze di un qualche pattern in un file.

La documentazione delle opzioni è su perldoc perlrun.