Awk è uno degli strumenti più vecchi nella cassetta degli attrezzi degli utenti Unix e Linux. Creato negli anni '70 da Alfred Aho, Peter Weinberger e Brian Kernighan (la A, W e K del nome dello strumento), awk è stato creato per l'elaborazione complessa di flussi di testo. È uno strumento complementare a sed, l'editor di flussi, progettato per l'elaborazione riga per riga di file di testo. Awk consente programmi strutturati più complessi ed è un linguaggio di programmazione completo.
Questo articolo spiegherà come utilizzare awk per attività più strutturate e complesse, inclusa una semplice applicazione di stampa unione.
Struttura del programma Awk
Uno script awk è costituito da blocchi funzionali circondati da {} (parentesi graffe). Esistono due blocchi funzione speciali, BEGIN e FINE , che vengono eseguiti prima dell'elaborazione della prima riga del flusso di input e dopo l'elaborazione dell'ultima riga. Nel mezzo, i blocchi hanno il formato:
pattern { action statements }
Ogni blocco viene eseguito quando la riga nel buffer di input corrisponde al modello. Se non è incluso alcun modello, il blocco funzione viene eseguito su ogni riga del flusso di input.
Inoltre, la seguente sintassi può essere utilizzata per definire funzioni in awk che possono essere richiamate da qualsiasi blocco:
function name(parameter list) { statements }
Questa combinazione di blocchi e funzioni di corrispondenza dei modelli consente allo sviluppatore di strutturare i programmi awk per il riutilizzo e la leggibilità.
Come awk elabora i flussi di testo
Awk legge il testo dal suo file di input o esegue lo streaming una riga alla volta e utilizza un separatore di campo per analizzarlo in un numero di campi. Nella terminologia awk, il buffer corrente è un record . Ci sono una serie di variabili speciali che influenzano il modo in cui awk legge ed elabora un file:
- FS (separatore di campo):per impostazione predefinita, questo è qualsiasi spazio bianco (spazi o tabulazioni)
- RS (separatore di record):per impostazione predefinita, una nuova riga (\n )
- NF (numero di campi):quando awk analizza una riga, questa variabile viene impostata sul numero di campi che sono stati analizzati
- $ 0: Il record corrente
- $ 1, $ 2, $ 3, ecc.: Il primo, secondo, terzo campo, ecc. dal record corrente
- NR (numero di record):il numero di record che sono stati analizzati finora dallo script awk
Ci sono molte altre variabili che influenzano il comportamento di awk, ma questo è sufficiente per cominciare.
Awk one-liners
Per uno strumento così potente, è interessante notare che la maggior parte dell'utilizzo di awk è costituito da battute di base. Forse il programma awk più comune stampa i campi selezionati da una riga di input da un file CSV, un file di registro, ecc. Ad esempio, il seguente one-liner stampa un elenco di nomi utente da /etc/passwd :
awk -F":" '{print $1 }' /etc/passwd
Come accennato in precedenza, $ 1 è il primo campo nel record corrente. Il -F opzione imposta la variabile FS sul carattere : .
Il separatore di campo può anche essere impostato in un blocco funzione BEGIN:
awk 'BEGIN { FS=":" } {print $1 }' /etc/passwd
Nell'esempio seguente, ogni utente la cui shell non è /sbin/nologin può essere stampato facendo precedere il blocco con un pattern match:
awk 'BEGIN { FS=":" } ! /\/sbin\/nologin/ {print $1 }' /etc/passwd
Awk avanzato:stampa unione
Ora che hai alcune nozioni di base, prova ad approfondire awk con un esempio più strutturato:creare una stampa unione.
Una stampa unione utilizza due file, uno (chiamato in questo esempio email_template.txt ) contenente un modello per un'email che desideri inviare:
From: Program committee <[email protected]>
To: {firstname} {lastname} <{email}>
Subject: Your presentation proposal
Dear {firstname},
Thank you for your presentation proposal:
{title}
We are pleased to inform you that your proposal has been successful! We
will contact you shortly with further information about the event
schedule.
Thank you,
The Program Committee
E l'altro è un file CSV (chiamato proposals.csv ) con le persone a cui vuoi inviare l'email:
firstname,lastname,email,title
Harry,Potter,[email protected],"Defeating your nemesis in 3 easy steps"
Jack,Reacher,[email protected],"Hand-to-hand combat for beginners"
Mickey,Mouse,[email protected],"Surviving public speaking with a squeaky voice"
Santa,Claus,[email protected],"Efficient list-making"
Vuoi leggere il file CSV, sostituire i campi rilevanti nel primo file (saltando la prima riga), quindi scrivere il risultato in un file chiamato acceptanceN.txt , incrementando N per ogni riga che analizzi.
Scrivi il programma awk in un file chiamato mail_merge.awk . Le dichiarazioni sono separate da ; negli script awk. Il primo compito consiste nell'impostare la variabile del separatore di campo e un paio di altre variabili necessarie allo script. Devi anche leggere ed eliminare la prima riga nel CSV, altrimenti verrà creato un file che inizia con Gentile nome . Per farlo, usa la funzione speciale getline e azzerare il contatore dei record dopo averlo letto.
BEGIN {
FS=",";
template="email_template.txt";
output="acceptance";
getline;
NR=0;
}
La funzione principale è molto semplice:per ogni riga elaborata viene impostata una variabile per i vari campi:nome , cognome , e-mail e titolo . Il file modello viene letto riga per riga e la funzione sub viene utilizzato per sostituire qualsiasi occorrenza delle sequenze di caratteri speciali con il valore della variabile pertinente. Quindi la riga, con le eventuali sostituzioni effettuate, viene inviata al file di output.
Poiché hai a che fare con il file modello e un file di output diverso per ogni riga, devi pulire e chiudere gli handle di file per questi file prima di elaborare il record successivo.
{
# Read relevant fields from input file
firstname=$1;
lastname=$2;
email=$3;
title=$4;
# Set output filename
outfile=(output NR ".txt");
# Read a line from template, replace special fields, and
# print result to output file
while ( (getline ln < template) > 0 )
{
sub(/{firstname}/,firstname,ln);
sub(/{lastname}/,lastname,ln);
sub(/{email}/,email,ln);
sub(/{title}/,title,ln);
print(ln) > outfile;
}
# Close template and output file in advance of next record
close(outfile);
close(template);
}
Hai finito! Esegui lo script sulla riga di comando con:
awk -f mail_merge.awk proposals.csv
o
awk -f mail_merge.awk < proposals.csv
e troverai i file di testo generati nella directory corrente.
Awk avanzato:conteggio della frequenza delle parole
Una delle funzionalità più potenti di awk è l'array associativo. Nella maggior parte dei linguaggi di programmazione, le voci di array sono in genere indicizzate da un numero, ma in awk, gli array sono referenziati da una stringa di chiave. Puoi memorizzare una voce dal file proposals.txt dalla sezione precedente. Ad esempio, in un singolo array associativo, come questo:
proposer["firstname"]=$1;
proposer["lastname"]=$2;
proposer["email"]=$3;
proposer["title"]=$4;
Ciò rende l'elaborazione del testo molto semplice. Un semplice programma che utilizza questo concetto è l'idea di un contatore di frequenza a parole. Puoi analizzare un file, suddividere le parole (ignorando la punteggiatura) in ogni riga, aumentare il contatore per ogni parola nella riga, quindi emettere le prime 20 parole che si trovano nel testo.
Innanzitutto, in un file chiamato wordcount.awk , imposta il separatore di campo su un'espressione regolare che includa spazi e segni di punteggiatura:
BEGIN {
# ignore 1 or more consecutive occurrences of the characters
# in the character group below
FS="[ .,:;()<>{}@!\"'\t]+";
}
Successivamente, la funzione del ciclo principale eseguirà un'iterazione su ciascun campo, ignorando eventuali campi vuoti (cosa che accade se è presente la punteggiatura alla fine di una riga) e incrementerà il conteggio delle parole per le parole nella riga.
{
for (i = 1; i <= NF; i++) {
if ($i != "") {
words[$i]++;
}
}
}
Infine, dopo che il testo è stato elaborato, usa la funzione END per stampare il contenuto dell'array, quindi usa la capacità di awk di inserire l'output in un comando shell per eseguire un ordinamento numerico e stampare le 20 parole più frequenti:
END {
sort_head = "sort -k2 -nr | head -n 20";
for (word in words) {
printf "%s\t%d\n", word, words[word] | sort_head;
}
close (sort_head);
}
L'esecuzione di questo script su una bozza precedente di questo articolo ha prodotto questo output:
[[email protected]]$ awk -f wordcount.awk < awk_article.txt
the 79
awk 41
a 39
and 33
of 32
in 27
to 26
is 25
line 23
for 23
will 22
file 21
we 16
We 15
with 12
which 12
by 12
this 11
output 11
function 11
Cosa c'è dopo?
Più risorse Linux
- Comandi Linux cheat sheet
- Cheat sheet sui comandi avanzati di Linux
- Corso online gratuito:Panoramica tecnica RHEL
- Cheat sheet della rete Linux
- Cheat sheet di SELinux
- Cheat sheet dei comandi comuni di Linux
- Cosa sono i container Linux?
- I nostri ultimi articoli su Linux
Se vuoi saperne di più sulla programmazione di awk, ti consiglio vivamente il libro Sed e awk di Dale Dougherty e Arnold Robbins.
Una delle chiavi per progredire nella programmazione awk è la padronanza delle "espressioni regolari estese". Awk offre diverse potenti aggiunte alla sintassi delle espressioni regolari sed con cui potresti già avere familiarità.
Un'altra grande risorsa per l'apprendimento di awk è la guida per l'utente di GNU awk. Ha un riferimento completo per la libreria di funzioni integrata di awk, oltre a molti esempi di script awk semplici e complessi.