Comando awk: stampare campi, delimitatore -F e pattern condizionali
Il comando awk è uno strumento Linux che legge un file di testo riga per riga, divide ogni riga in campi in base agli spazi o al delimitatore che indichi e seleziona solo i campi delle righe che soddisfano una condizione per stamparli o farci dei calcoli. Su un server AlmaLinux 9 abbiamo contato le richieste per IP e il numero per codice di stato da un file di esempio con il log degli accessi di un server web con una sola riga collegata a sort tramite pipe: le tre risposte 404 delle dodici righe erano tutte richieste a wp-login.php e .env provenienti da un solo IP.
L'output seguente è stato ottenuto con l'account root in una cartella di lavoro su un server AlmaLinux 9.8. Il log degli accessi, l'elenco degli account, i dati di vendita e il file dei punteggi sono file di esempio creati per il test, e anche gli indirizzi IP e i nomi degli account al loro interno sono valori di esempio.
1️⃣ Uso di base del comando awk
awk divide ogni riga letta in base agli spazi e numera i campi dall'inizio come $1, $2, $3. L'abbiamo verificato con scores.txt, dove nome, risultato e punteggio sono separati da spazi.
cat scores.txtalice pass 82
bob fail 47
carol pass 91
dave pass 68
erin fail 55Le parentesi graffe dentro gli apici singoli contengono l'azione eseguita su ogni riga, e se dopo print elenchi i numeri di campo separati da virgole, escono solo quei campi. Stampando il primo e il terzo campo restano solo nome e punteggio.
awk '{print $1, $3}' scores.txtalice 82
bob 47
carol 91
dave 68
erin 55Se togli la virgola tra i campi e scrivi $1 $3, i due valori escono attaccati senza spazio. La virgola di print inserisce il separatore di output (uno spazio per impostazione predefinita), mentre i valori scritti uno accanto all'altro senza virgola si uniscono in un'unica stringa.
awk '{print $1 $3}' scores.txtalice82
bob47
carol91
dave68
erin55NR è il numero della riga letta finora, NF il numero di campi di quella riga. $NF indica l'ultimo campo, quindi puoi scegliere l'ultima colonna anche se il numero di campi cambia da riga a riga.
awk '{print NR, NF, $NF}' scores.txt1 3 82
2 3 47
3 3 91
4 3 68
5 3 55Anche il log degli accessi di un server web è separato da spazi, quindi i campi si scelgono allo stesso modo. I numeri di campo della prima riga del log di test sono i seguenti.
| Campo | Valore nella prima riga | Significato |
|---|---|---|
| $1 | 192.0.2.10 | IP del client |
| $4 | [29/Oct/2026:00:01:12 | Data e ora della richiesta |
| $6 | "GET | Metodo della richiesta (preceduto da virgolette doppie) |
| $7 | /index.html | URL della richiesta |
| $9 | 200 | Codice di stato |
| $10 | 5120 | Byte inviati |
| $11 | "-" | Pagina precedente (Referer) |
| $12~$15 | "Mozilla/5.0 (X11; Linux x86_64)" | User agent (diviso a ogni spazio) |
Abbiamo stampato $1, $7 e $9, che corrispondono a IP del client, URL della richiesta e codice di stato.
awk '{print $1, $7, $9}' access.log192.0.2.10 /index.html 200
198.51.100.7 /about.html 200
192.0.2.10 /images/logo.png 200
203.0.113.25 /wp-login.php 404
203.0.113.25 /wp-login.php 404
192.0.2.10 /contact.html 200
198.51.100.7 /old-page.html 301
203.0.113.25 /.env 404
192.0.2.44 /index.html 200
198.51.100.7 /api/search?q=awk 500
192.0.2.44 /images/logo.png 304
192.0.2.10 /index.html 200Una colonna con spazi nel valore, come lo user agent, viene divisa in più campi. NF era 15 in tutte le prime tre righe, e $NF è l'ultimo pezzo dello user agent.
awk '{print NF, $NF}' access.log | head -315 x86_64)"
15 x86_64)"
15 x86_64)"2️⃣ Impostare il delimitatore con awk -F
Per un file con colonne separate da un simbolo diverso dallo spazio, scrivi quel simbolo dopo -F. Stampando $1 senza -F dall'elenco account users.txt, creato nella stessa forma di /etc/passwd, la maggior parte delle righe è uscita intera.
awk '{print $1}' users.txtroot:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
nobody:x:65534:65534:Kernel
user:x:1000:1000:Example
deploy:x:1001:1001:Deploy
backup:x:1002:1002::/home/backup:/sbin/nologinIl delimitatore predefinito è lo spazio, quindi una riga unita solo dai due punti ha un unico campo. Solo le righe nobody, user e deploy, che hanno uno spazio nella colonna descrizione, vengono tagliate in quello spazio.
Con -F: i due punti diventano il delimitatore, e nome account ($1) e shell di login ($7) si separano.
awk -F: '{print $1, $7}' users.txtroot /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
nobody /sbin/nologin
user /bin/bash
deploy /bin/bash
backup /sbin/nologinI file CSV separati da virgole si dividono con -F,. Stampando articolo e quantità dai dati di vendita di sales.csv, anche l'intestazione della prima riga (item qty) viene divisa allo stesso modo e compare insieme.
awk -F, '{print $2, $3}' sales.csvitem qty
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2-F, non è adatto ai CSV con virgole dentro colonne tra virgolette doppie. In quoted.csv, con virgole nella colonna articolo, i campi vengono divisi anche alle virgole tra virgolette, e nel secondo campo resta solo la parte iniziale del nome dell'articolo.
awk -F, '{print $2}' quoted.csvitem
"cable
keyboardPer questi file, il manuale ufficiale di GNU Awk indica la variabile FPAT. Se al posto del delimitatore scrivi la forma di un campo come espressione regolare, una colonna tra virgolette esce come un solo campo.
awk -v FPAT='([^,]*)|("[^"]*")' '{print $2}' quoted.csvitem
"cable, usb"
keyboardPer usare più delimitatori, scrivili come espressione regolare tra parentesi quadre. Con i due punti e la barra come delimitatori, $NF diventa l'ultimo nome nel percorso della shell di login (bash, nologin).
awk -F'[:/]' '{print $1, $NF}' users.txtroot bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin3️⃣ Pattern condizionali del comando awk
Anche scegliendo i campi vengono stampate tutte le righe, quindi per tenere solo quelle necessarie metti una condizione (pattern) prima delle parentesi graffe. Una stringa tra barre seleziona solo le righe che la contengono, e se ometti l'azione viene stampata la riga intera.
awk '/wp-login/' access.log203.0.113.25 - - [29/Oct/2026:00:04:51 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"
203.0.113.25 - - [29/Oct/2026:00:04:52 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"Le righe selezionate sono le stesse del comando grep, e awk può scegliere direttamente i campi di quelle righe e stamparli. Passando ad awk tramite pipe le stesse due righe selezionate con grep, restano solo IP e codice di stato.
grep wp-login access.log | awk '{print $1, $9}'203.0.113.25 404
203.0.113.25 404I valori dei campi si confrontano con ==, !=, >, >=, <, <=. Abbiamo selezionato solo le righe con codice di stato ($9) 404 e stampato IP e URL della richiesta.
awk '$9 == 404 {print $1, $7}' access.log203.0.113.25 /wp-login.php
203.0.113.25 /wp-login.php
203.0.113.25 /.envPer confrontare con una stringa, racchiudi il valore tra virgolette doppie. Abbiamo selezionato solo i nomi il cui secondo campo è pass.
awk '$2 == "pass" {print $1}' scores.txtalice
carol
daveIl confronto numerico serve per selezionare per intervallo, come con i numeri di account (UID). Selezionando gli account con UID pari o superiore a 1000, insieme agli account normali entra anche nobody, con UID 65534.
awk -F: '$3 >= 1000 {print $1, $3}' users.txtnobody 65534
user 1000
deploy 1001
backup 1002Due condizioni si uniscono con &&. Aggiungendo la condizione inferiore a 65534, nobody esce e restano solo i tre account normali e le loro cartelle home.
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' users.txtuser /home/user
deploy /home/deploy
backup /home/backup[~] controlla se un campo corrisponde a un'espressione regolare, [!~] se non corrisponde. Selezionando gli account con shell di login ($7) che finisce in bash e quelli diversi da nologin, compaiono gli stessi tre account.
awk -F: '$7 ~ /bash$/ {print $1}' users.txtroot
user
deployawk -F: '$7 !~ /nologin/ {print $1, $7}' users.txtroot /bin/bash
user /bin/bash
deploy /bin/bashLa condizione NR seleziona per numero di riga. Con NR > 1 si toglie la riga di intestazione del CSV e restano solo i dati di vendita.
awk -F, 'NR > 1 {print $2, $3}' sales.csvkeyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2Anche l'output di altri comandi si può passare tramite pipe e dividere allo stesso modo. Scegliendo dimensione ($5) e nome ($9) dall'output di ls -l, la prima riga è uscita vuota, perché la prima riga di ls -l, [total 24], non ha quinto e nono campo.
ls -l | awk '{print $5, $9}'
1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txtCon NR > 1 la prima riga scompare e restano solo dimensioni e nomi dei file.
ls -l | awk 'NR > 1 {print $5, $9}'1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txt4️⃣ awk BEGIN, END, somme e medie
Il blocco BEGIN viene eseguito una volta prima di leggere la prima riga, il blocco END una volta dopo aver elaborato l'ultima. Per una somma, aggiungi a una variabile a ogni riga e stampala in END. Il diagramma seguente mostra l'ordine che awk segue a ogni riga per sommare gli importi dei dati di vendita.
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csvLa somma di tutti i byte inviati ($10) del log degli accessi è stata di 41298 byte. Una variabile usata per la prima volta parte vuota (0 come numero), quindi non serve impostarla a 0.
awk '{sum += $10} END {print sum}' access.log41298Se stampi la somma nell'azione di ogni riga invece che in END, ogni riga mostra il totale parziale. Stampando il numero di riga e t a ogni somma di quantità per prezzo, escono sette righe senza l'intestazione, e l'886.3 dell'ultima riga è il totale complessivo.
awk -F, 'NR > 1 {t += $3 * $4; print NR, t}' sales.csv2 90
3 189.5
4 378.5
5 423.5
6 468.5
7 508.3
8 886.3La media si calcola in END dividendo la somma per il numero di righe. La somma dei cinque punteggi era 343 e la media 68.6.
awk '{total += $3; n++} END {print total, n, total / n}' scores.txt343 5 68.6BEGIN serve a stampare una volta una riga di intestazione sopra i risultati. Sopra articoli e importi (quantità × prezzo unitario) la riga item total compare una sola volta.
awk -F, 'BEGIN {print "item", "total"} NR > 1 {print $2, $3 * $4}' sales.csvitem total
keyboard 90
mouse 99.5
monitor 189
keyboard 45
cable 45
mouse 39.8
monitor 378Il separatore di output si cambia con la variabile OFS in BEGIN. Impostando OFS su una virgola, al posto delle virgole di print entra una virgola invece di uno spazio, e l'elenco account esce in formato CSV.
awk 'BEGIN {FS = ":"; OFS = ","} {print $1, $3, $7}' users.txtroot,0,/bin/bash
bin,1,/sbin/nologin
daemon,2,/sbin/nologin
nobody,65534,/sbin/nologin
user,1000,/bin/bash
deploy,1001,/bin/bash
backup,1002,/sbin/nologinPer allineare le colonne usa printf. %-10s è una stringa allineata a sinistra su 10 posizioni, %3d un intero su 3 posizioni e %8.2f un numero su 8 posizioni con due decimali.
awk -F, 'NR > 1 {printf "%-10s %8.2f\n", $2, $3, $3 * $4}' sales.csvkeyboard 2 90.00
mouse 5 99.50
monitor 1 189.00
keyboard 1 45.00
cable 10 45.00
mouse 2 39.80
monitor 2 378.00A differenza di print, printf non va a capo, quindi metti [\n] alla fine del formato. Senza [\n], i nomi degli articoli escono attaccati su una riga.
awk -F, 'NR > 1 {printf "%s ", $2}' sales.csvkeyboard mouse monitor keyboard cable mouse monitor5️⃣ Analisi dei log e opzioni del comando awk
🔲 Analisi dei log
Sommare in una sola variabile dà un solo totale, quindi per contare per IP o per codice di stato raccogli i valori in un array. n[$1]++ aggiunge 1 alla posizione dell'array con l'IP come chiave, e il for (ip in n) di END estrae le posizioni una alla volta.
awk '{n[$1]++} END {for (ip in n) print n[ip], ip}' access.log | sort -rn4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44L'ordine in cui for (ip in n) le estrae non è definito (secondo il manuale di GNU Awk), quindi abbiamo ordinato per numero di richieste con sort -rn. 192.0.2.10 è in testa con 4 richieste.
Il numero per codice di stato si conta allo stesso modo. C'erano 3 risposte 404 e 1 risposta 500.
awk '{n[$9]++} END {for (s in n) print s, n[s]}' access.log | sort200 6
301 1
304 1
404 3
500 1Estraendo solo i codici di stato e passandoli a sort e uniq -c si ottengono gli stessi numeri.
awk '{print $9}' access.log | sort | uniq -c 6 200
1 301
1 304
3 404
1 500Per vedere solo le risposte di errore, metti una condizione sul codice di stato. Le righe da 400 in su erano tre richieste in cerca di wp-login.php e .env e un errore 500.
awk '$9 >= 400 {print $9, $1, $7}' access.log404 203.0.113.25 /wp-login.php
404 203.0.113.25 /wp-login.php
404 203.0.113.25 /.env
500 198.51.100.7 /api/search?q=awkAnche il totale dei byte inviati per IP si calcola sommando in un array. 192.0.2.10 è quello che ha ricevuto di più, con 31340 byte.
awk '{b[$1] += $10} END {for (ip in b) print b[ip], ip}' access.log | sort -rn31340 192.0.2.10
5120 192.0.2.44
4250 198.51.100.7
588 203.0.113.25Il campo dell'ora ($4) si può confrontare come stringa. Tra righe con la stessa data e lo stesso formato, l'ordine delle stringhe coincide con quello orario, quindi abbiamo selezionato solo le quattro richieste dalle 00:05 a poco prima delle 00:10.
awk '$4 >= "[29/Oct/2026:00:05" && $4 < "[29/Oct/2026:00:10" {print $4, $7}' access.log[29/Oct/2026:00:05:30 /contact.html
[29/Oct/2026:00:07:02 /old-page.html
[29/Oct/2026:00:07:15 /.env
[29/Oct/2026:00:09:48 /index.html🔲 Opzioni del comando awk
📌 Opzioni principali: risultati di questo articolo
| Opzione | Compito | Risultato in questo articolo |
|---|---|---|
| -F fs | Imposta il separatore dei campi di input | -F: separa nome account ($1) e shell di login ($7) di users.txt; -F, stampa articolo e quantità di sales.csv insieme all'intestazione (item qty) |
| -v var=val | Assegna un valore a una variabile prima dell'esecuzione del programma | Passando la variabile di shell min con -v min=$min si stampano sei righe con almeno 1000 byte inviati; con -v FPAT=… una colonna tra virgolette di quoted.csv esce come un solo campo |
| -i inplace | Salva l'output nel file originale invece che sullo schermo | Dopo gawk -i inplace non compare nulla sullo schermo e la copia di scores.txt resta con due colonne, nome e punteggio; aggiungendo -v inplace::suffix=.bak il contenuto precedente resta in un file .bak |
| --version | Stampa le informazioni sulla versione | La prima riga di [awk --version] mostra la versione GNU Awk 5 |
📎 Altre opzioni: secondo il manuale ufficiale
| Opzione | Compito |
|---|---|
| -f program-file | Legge il programma AWK da un file invece che dal primo argomento della riga di comando; -f si può indicare più volte |
| -e program-text | Usa il testo sulla riga di comando come sorgente del programma AWK; si può usare con funzioni di libreria caricate da -f o --include |
| -E file | Simile a -f, ma viene elaborata per ultima e blocca le assegnazioni di variabili da riga di comando; per script #! |
| --traditional | Esegue in modalità di compatibilità, senza riconoscere le estensioni GNU |
| --posix | Attiva la modalità di compatibilità e aggiunge restrizioni, ad esempio non riconosce func, sinonimo di function |
| --lint | Avvisa di costrutti dubbi o difficili da portare su altre implementazioni di AWK; con --lint=fatal gli avvisi diventano errori fatali |
| --dump-variables | Scrive in un file un elenco ordinato delle variabili globali con tipo e valore finale; awkvars.out se non si indica un file |
🔲 Simulatore del comando awk
Nel simulatore qui sotto puoi digitare comandi awk sugli stessi file di test.
🔢 FAQ & contenuti consigliati
Ordinamento
Comando sort di Linux: ordinare i file
Il comando sort ordina il contenuto di un file in ordine alfabetico o numerico. Usalo subito dopo per vedere i totali di awk dal più grande al più piccolo.
Rimozione dei duplicati
Comando uniq di Linux
Il comando uniq riduce a una sola le righe uguali consecutive e, con -c, conta quante volte si sono ripetute.
Visualizzazione dei file
Comandi head e tail di Linux
I comandi head e tail mostrano solo le prime o le ultime righe di un file. Si usano spesso per controllare il log degli accessi di un server.
ℹ️ Informativa sull'affiliazione
I contenuti di questo sito includono link di affiliazione. Se un visitatore acquista un prodotto o un servizio tramite uno di questi link, il sito riceve una commissione dal venditore. In questo processo l'importo pagato dall'acquirente (durante le promozioni l'importo scende↓) non aumenta. Prezzi, sconti e disponibilità indicati si riferiscono al momento della stesura e possono differire da quelli reali: prima di acquistare verifica sempre presso il venditore. I prodotti vengono scelti e valutati secondo criteri propri, e le commissioni non influiscono sull'ordine di presentazione né sul contenuto delle valutazioni.