Comando awk: stampare campi, delimitatore -F e pattern condizionali

Ambiente di testFamiglia RHELAlmaLinux 9.8account rootVerificato il 2026-10-29

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

  • ↪️ Numeri di campo e print

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.txt
alice   pass  82
bob     fail  47
carol   pass  91
dave    pass  68
erin    fail  55

Le 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.txt
alice 82
bob 47
carol 91
dave 68
erin 55

Se 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.txt
alice82
bob47
carol91
dave68
erin55

NR è 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.txt
1 3 82
2 3 47
3 3 91
4 3 68
5 3 55

Anche 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.

CampoValore nella prima rigaSignificato
$1192.0.2.10IP del client
$4[29/Oct/2026:00:01:12Data e ora della richiesta
$6"GETMetodo della richiesta (preceduto da virgolette doppie)
$7/index.htmlURL della richiesta
$9200Codice di stato
$105120Byte 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.log
192.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 200

Una 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 -3
15 x86_64)"
15 x86_64)"
15 x86_64)"

2️⃣ Impostare il delimitatore con awk -F

  • ↪️ Delimitatori due punti, virgola ed espressione regolare

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.txt
root: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/nologin

Il 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.txt
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
nobody /sbin/nologin
user /bin/bash
deploy /bin/bash
backup /sbin/nologin

I 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.csv
item 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.csv
item
"cable
keyboard

Per 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.csv
item
"cable, usb"
keyboard

Per 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.txt
root bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin

3️⃣ Pattern condizionali del comando awk

  • ↪️ Espressioni regolari, confronti e NR

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.log
203.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 404

I 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.log
203.0.113.25 /wp-login.php
203.0.113.25 /wp-login.php
203.0.113.25 /.env

Per 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.txt
alice
carol
dave

Il 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.txt
nobody 65534
user 1000
deploy 1001
backup 1002

Due 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.txt
user /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.txt
root
user
deploy
awk -F: '$7 !~ /nologin/ {print $1, $7}' users.txt
root /bin/bash
user /bin/bash
deploy /bin/bash

La 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.csv
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2

Anche 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.txt

Con 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.txt

4️⃣ awk BEGIN, END, somme e medie

  • ↪️ Variabili e formati di output di printf

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.

🔁 Ordine in cui awk elabora sales.csv riga per riga
Diagramma esplicativo
🔁 Ordine di elaborazione
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csv
① Riga lettaNR = 1date,item,qty,priceNR = 22026-10-01,keyboard,2,45.00NR = 32026-10-01,mouse,5,19.90NR = 82026-10-04,monitor,2,189.00Nessun'altra riga da leggereLa riga 8 è l'ultima.
② Divisione dei campi (-F,)$1date$2item$3qty$4priceAnche la riga di intestazione viene divisa allo stesso modo.$12026-10-01$2keyboard$32$445.00$3 quantità, $4 prezzo unitario$12026-10-01$2mouse$35$419.90$3 quantità, $4 prezzo unitario$12026-10-04$2monitor$32$4189.00$3 quantità, $4 prezzo unitarioNessuna riga da dividereIl blocco END non legge righe.
③ Pattern e azioneNR > 1 falsoL'azione viene saltata.t = (vuoto)NR > 1 verot += 2 * 45.00t = 90NR > 1 verot += 5 * 19.90t = 189.5NR > 1 verot += 2 * 189.00t = 886.3Esecuzione di ENDtotal 886.30printf stampa t con due decimali.
La prima riga è l'intestazione, quindi NR > 1 è falso e awk passa alla riga successiva senza eseguire l'azione.
Dalla seconda riga il pattern è vero, e 90, la quantità per il prezzo unitario, viene sommato alla variabile t.
La variabile t mantiene il valore da una riga all'altra, quindi la somma fino alla terza riga è 189.5.
Le righe da 4 a 7 seguono lo stesso ordine, e fino all'ultima, la riga 8, t arriva a 886.3.
Dopo l'elaborazione dell'ultima riga, il blocco END viene eseguito una volta e printf stampa total 886.30.

La 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.log
41298

Se 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.csv
2 90
3 189.5
4 378.5
5 423.5
6 468.5
7 508.3
8 886.3

La 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.txt
343 5 68.6

BEGIN 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.csv
item total
keyboard 90
mouse 99.5
monitor 189
keyboard 45
cable 45
mouse 39.8
monitor 378

Il 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.txt
root,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/nologin

Per 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.csv
keyboard     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.00

A 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.csv
keyboard mouse monitor keyboard cable mouse monitor

5️⃣ Analisi dei log e opzioni del comando awk

  • ↪️ Conteggio di IP e codici di stato, riepilogo delle opzioni

🔲 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 -rn
4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44

L'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 | sort
200 6
301 1
304 1
404 3
500 1

Estraendo 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 500

Per 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.log
404 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=awk

Anche 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 -rn
31340 192.0.2.10
5120 192.0.2.44
4250 198.51.100.7
588 203.0.113.25

Il 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

OpzioneCompitoRisultato in questo articolo
-F fsImposta 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=valAssegna un valore a una variabile prima dell'esecuzione del programmaPassando 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 inplaceSalva l'output nel file originale invece che sullo schermoDopo 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
--versionStampa le informazioni sulla versioneLa prima riga di [awk --version] mostra la versione GNU Awk 5

📎 Altre opzioni: secondo il manuale ufficiale

OpzioneCompito
-f program-fileLegge il programma AWK da un file invece che dal primo argomento della riga di comando; -f si può indicare più volte
-e program-textUsa il testo sulla riga di comando come sorgente del programma AWK; si può usare con funzioni di libreria caricate da -f o --include
-E fileSimile a -f, ma viene elaborata per ultima e blocca le assegnazioni di variabili da riga di comando; per script #!
--traditionalEsegue in modalità di compatibilità, senza riconoscere le estensioni GNU
--posixAttiva la modalità di compatibilità e aggiunge restrizioni, ad esempio non riconosce func, sinonimo di function
--lintAvvisa di costrutti dubbi o difficili da portare su altre implementazioni di AWK; con --lint=fatal gli avvisi diventano errori fatali
--dump-variablesScrive 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.

Simulatore del comando awkGNU Awk 5
[root@server work]#
Un piccolo interprete awk che funziona solo nel tuo browser. I quattro file di test sono gli stessi usati per le misurazioni, e con ↑↓ richiami i comandi precedenti.

🔢 FAQ & contenuti consigliati

La shell non sostituisce $min dentro gli apici singoli, che arriva ad awk così com'è, quindi passalo con l'opzione -v. Se metti 1000 nella variabile di shell min e passi -v min=$min, escono le sei righe con almeno 1000 byte inviati. Se racchiudi il programma awk tra virgolette doppie, la shell sostituisce prima perfino il $1 di [print $1] con un valore vuoto, e viene stampata la riga intera.

Con -i inplace, GNU Awk salva l'output nel file originale invece che sullo schermo. Eseguendo con gawk -i inplace un programma che stampa il primo e il terzo campo su una copia di scores.txt, sullo schermo non è comparso nulla e il file è rimasto con due colonne, nome e punteggio. Poiché il file originale viene sovrascritto direttamente, aggiungendo -v inplace::suffix=.bak il contenuto precedente resta in un file .bak.

Sul server AlmaLinux 9, /usr/bin/awk era un collegamento simbolico a gawk, e la prima riga di [awk --version] mostrava la versione GNU Awk 5. Su questo server i due nomi eseguono lo stesso programma. Prima di usare funzioni di GNU Awk come -i inplace o FPAT, verifica con [awk --version] che si tratti di GNU Awk.

ℹ️ 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.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Dimostra di essere umano: 5   +   6   =