Guida

grep, sed e awk: esempi di elaborazione del testo nel terminale Linux

Aggiornato: · Skillaria.Top

grep trova le righe che corrispondono a un modello (grep -rn error /var/log), sed modifica il testo nel flusso o nel file (sed -i 's/old/new/g' file), awk lavora con le colonne e fa i conteggi (awk '{print $1}' access.log). Insieme in una pipeline sostituiscono uno script: grep filtra le righe, sed corregge il testo, awk estrae i campi e aggrega.

grep, sed e awk sono tre utility classiche di Linux per l'elaborazione del testo. grep trova le righe che corrispondono a un modello, sed sostituisce ed elimina il testo nel flusso senza aprire un editor, mentre awk suddivide la riga in colonne e sa fare dei calcoli. Qui sotto trovi gli esempi di grep, sed e awk che servono davvero quando si analizzano log, file di configurazione e output di comandi, insieme agli errori più comuni.

Quando usare quale strumento

Tutti e tre leggono il testo riga per riga e scrivono il risultato nell'output standard, quindi si possono collegare facilmente con |. La differenza sta nel compito che ciascuno risolve meglio.

CompitoStrumentoEsempio
Trovare righe con un testogrepgrep ERROR app.log
Sostituire un testosedsed 's/http:/https:/g' file
Eliminare o mostrare righe per numerosedsed -n '10,20p' file
Estrarre una colonnaawkawk '{print $1}' access.log
Calcolare somma, media, raggruppamentiawkawk '{s+=$10} END {print s}'

Regola semplice: se devi solo filtrare, usa grep; se devi modificare il testo, usa sed; se il testo ha colonne o serve aritmetica, usa awk.

grep: ricerca di righe per modello

Esempi di base con grep

# Righe con la parola error nel file
grep error /var/log/syslog

# Senza distinzione tra maiuscole e minuscole: Error, ERROR, error
grep -i error app.log

# Con i numeri di riga
grep -n "Connection refused" app.log

# Ricorsivamente in una directory, con i nomi dei file
grep -rn "DB_HOST" /etc/myapp/

# Solo i nomi dei file in cui c'è una corrispondenza
grep -rl "TODO" src/

# Invertire: tutto TRANNE commenti e righe vuote
grep -v -e '^#' -e '^$' /etc/ssh/sshd_config

# Contare le righe corrispondenti
grep -c " 500 " access.log

Il flag -w cerca la parola intera: grep -w log non trova login. Il flag -F disattiva le espressioni regolari e cerca la stringa alla lettera, utile quando il modello contiene punti, parentesi o asterischi.

Contesto attorno alla riga trovata

Un errore in un log raramente si capisce da una sola riga: serve lo stack o la richiesta che lo ha causato. Per questo esistono i flag di contesto:

# 3 righe dopo la corrispondenza (After)
grep -A 3 "Traceback" app.log

# 2 righe prima (Before)
grep -B 2 "OOM" /var/log/kern.log

# 5 righe da entrambi i lati (Context)
grep -C 5 "segfault" /var/log/syslog

grep separa i gruppi di corrispondenze con una riga --, così l'output è facile da leggere.

grep e le espressioni regolari

Per impostazione predefinita grep capisce le espressioni regolari di base (BRE). Con il flag -E si attivano quelle estese (ERE), in cui +, ?, | e le parentesi funzionano senza barre rovesciate.

CostruttoSignificatoEsempio
^inizio della rigagrep '^root' /etc/passwd
$fine della rigagrep 'bash$' /etc/passwd
.qualsiasi caratteregrep 'h.t'
*da 0 ripetizioni in sugrep 'ab*c'
+ (ERE)1 o piùgrep -E '[0-9]+'
| (ERE)oppuregrep -E 'error|warn'
[...]un carattere dall'insiemegrep '[Ee]rror'
# Righe con errori o avvisi
grep -E "ERROR|WARN" app.log

# Solo gli indirizzi IP dal log, uno per riga (-o stampa solo la corrispondenza)
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log

grep -P: estrarre un valore tra parti note

GNU grep ha il flag -P, che usa espressioni regolari in stile Perl. Con esso sono disponibili \K («dimentica tutto ciò che è stato trovato fino a questo punto») e il lookahead, che permettono di estrarre solo il valore:

$ echo 'user_id=4821 status=ok' | grep -oP 'user_id=\K[0-9]+'
4821

$ grep -oP '"version":\s*"\K[^"]+' package.json
2.4.1

Il flag -P non è presente in tutte le versioni di grep (ad esempio nel grep BSD di macOS manca), quindi negli script portabili è meglio usare sed o awk.

grep -q nelle condizioni degli script

grep -q non stampa nulla e restituisce solo il codice di uscita: 0 se trova, 1 se non trova. Così il controllo si scrive direttamente dentro if:

if grep -q "^PermitRootLogin no" /etc/ssh/sshd_config; then
  echo "l'accesso root è disattivato"
fi

È più breve che salvare l'output in una variabile e verificare che sia vuota, e grep si ferma alla prima corrispondenza.

sed: sostituzione ed eliminazione del testo

Sostituzione: il comando s

Il comando principale di sed è s/modello/sostituzione/flag. Senza il flag g viene sostituita solo la prima corrispondenza in ogni riga.

# Stampa il testo con la sostituzione (il file non cambia)
sed 's/foo/bar/' file.txt

# Sostituisce tutte le occorrenze nella riga
sed 's/foo/bar/g' file.txt

# Senza distinguere maiuscole e minuscole (GNU sed)
sed 's/error/ERROR/gI' app.log

# Un altro separatore, se nel testo ci sono barre
sed 's|/var/www|/srv/www|g' nginx.conf

sed -i: sostituzione direttamente nel file

Il flag -i scrive il risultato nel file originale. Per non perdere l'originale, indica un suffisso per la copia di backup:

# Crea config.ini.bak e modifica config.ini
sed -i.bak 's/^debug=true/debug=false/' config.ini

# Sostituzione in tutti i .conf di una directory
find /etc/myapp -name "*.conf" -exec sed -i 's/old-host/new-host/g' {} +

Prima di una modifica massiva, esegui il comando senza -i e guarda l'output. Su macOS (BSD sed) l'opzione -i richiede un argomento: sed -i '' 's/a/b/' file.

Lavorare con le righe per numero e per modello

# Mostra le righe 10–20 (-n disattiva la stampa automatica, p = stampa)
sed -n '10,20p' file.txt

# Elimina la riga 5
sed '5d' file.txt

# Elimina l'ultima riga
sed '$d' file.txt

# Elimina righe vuote e commenti
sed -e '/^$/d' -e '/^#/d' nginx.conf

# Sostituisce solo nelle righe che contengono "listen"
sed '/listen/s/80/8080/' nginx.conf

# Inserisce una riga dopo la corrispondenza (GNU sed)
sed '/^\[mysqld\]/a max_connections = 500' my.cnf

Riferimenti all'indietro e &

La parte del modello tra parentesi può essere riutilizzata nella sostituzione con \1, \2, mentre il simbolo & indica l'intero testo trovato.

# Scambia nome e cognome (ERE con -E)
$ echo "Rossi Mario" | sed -E 's/(\S+) (\S+)/\2 \1/'
Mario Rossi

# Racchiude tutti i numeri tra parentesi quadre
$ echo "port 8080, timeout 30" | sed -E 's/[0-9]+/[&]/g'
port [8080], timeout [30]

# Converte la data 2026-10-14 in 14.10.2026
$ echo 2026-10-14 | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'
14.10.2026

awk: colonne, condizioni e conteggi

Stampare la colonna che ti serve

awk divide ogni riga in campi separati da spazi e tabulazioni: $1 è il primo campo, $2 il secondo, $NF l'ultimo, $0 l'intera riga.

# Indirizzi IP dal log di nginx (prima colonna)
awk '{print $1}' /var/log/nginx/access.log

# Nome utente e shell da /etc/passwd (separatore: due punti)
awk -F: '{print $1, $7}' /etc/passwd

# Ultima colonna
df -h | awk '{print $NF}'

# Separatore personalizzato in output
awk -F: -v OFS=';' '{print $1, $3}' /etc/passwd

Condizioni: stampare solo le righe giuste

Prima del blocco tra parentesi graffe si può mettere una condizione: awk esegue l'azione solo per le righe che la soddisfano.

# Utenti con UID da 1000 in su (utenti normali, non di sistema)
awk -F: '$3 >= 1000 {print $1}' /etc/passwd

# Richieste con codice di risposta 500 o superiore (campo 9 nel formato combined)
awk '$9 >= 500' access.log

# Righe in cui la colonna 6 corrisponde a un'espressione regolare
awk '$6 ~ /POST/' access.log

# File più grandi di 100 MB dall'output di ls -l
ls -l | awk '$5 > 100*1024*1024 {print $9}'

BEGIN, END e NR: awk come un piccolo programma

Il blocco BEGIN viene eseguito prima di leggere la prima riga, END dopo l'ultima. NR contiene il numero della riga corrente, e nel blocco END il numero totale di righe.

# Somma delle dimensioni delle risposte (campo 10) in megabyte
awk '{sum += $10} END {printf "%.1f MB\n", sum/1024/1024}' access.log

# Tempo medio di risposta dall'ultima colonna
awk '{s += $NF} END {if (NR) print s/NR}' timings.log

# Salta l'intestazione di un CSV
awk -F, 'NR > 1 {print $2}' users.csv

# Conteggio delle richieste per IP: array associativo
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head

L'ultimo esempio è un classico dell'analisi dei log: l'array count con chiave IP sostituisce la combinazione sort | uniq -c ed è facile da estendere, ad esempio al traffico totale per ogni indirizzo.

Pipeline: grep, sed e awk insieme

La vera forza sta nella combinazione. Ogni strumento fa la sua parte, e | passa il risultato al successivo.

I 10 IP con più errori 5xx

grep ' 50[0-9] ' /var/log/nginx/access.log \
  | awk '{print $1}' \
  | sort | uniq -c | sort -rn | head -10
    412 203.0.113.45
    118 198.51.100.7
     36 192.0.2.19

Qui grep elimina in modo grossolano le righe, awk estrae il campo, e sort | uniq -c conta le ripetizioni. Lo si può scrivere anche con un solo awk: awk '$9 ~ /^5/ {c[$1]++} END {for (i in c) print c[i], i}' access.log | sort -rn | head. È più preciso, perché controlla proprio la colonna del codice di risposta.

Quali URL vengono aperti più spesso

awk '{print $7}' access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head

sed qui taglia la parte di query string, così /search?q=a e /search?q=b vengono contati come una sola pagina.

Valori da un file di configurazione senza commenti

grep -v '^\s*#' /etc/myapp/app.env | grep '=' | awk -F= '{print $1}'

Errori in un'ora precisa

grep '14/Oct/2026:03:' access.log | awk '$9 >= 500 {print $4, $7, $9}'

Errori tipici

  • Virgolette doppie invece di singole in awk. In awk "{print $1}" la shell sostituisce la propria variabile $1 prima che awk parta. Scrivi il programma awk e le espressioni sed tra virgolette singole.
  • Dimenticare g in sed. Senza di esso viene sostituita solo la prima corrispondenza nella riga, le altre restano.
  • sed -i senza verifica. Un'espressione regolare sbagliata rovina il file in silenzio. Prima esegui senza -i oppure usa -i.bak.
  • + e | non funzionano in grep. È sintassi estesa: serve grep -E (o sed -E).
  • Il punto nel modello. grep 192.168.1.1 trova anche 192x168y1z1: il punto significa «qualsiasi carattere». Usa grep -F oppure scappa il punto con \..
  • Spazi nelle colonne. Se un campo può contenere spazi (CSV, percorsi), imposta il separatore in modo esplicito: awk -F, oppure awk -F'\t'.
  • Cat inutile. cat file | grep x funziona, ma grep x file è più corto e non avvia un processo in più.

Con cosa sostituire grep, sed e awk

Per il JSON queste utility sono scomode: la struttura è meglio analizzarla con jq (jq '.items[] | select(.status=="active") | .name'), e lo YAML con yq. Per la ricerca ricorsiva veloce nei progetti grandi si usa spesso ripgrep (comando rg), che di default rispetta .gitignore. Se preferisci la sintassi di Perl, perl -pe 's/a/b/g' e perl -lane 'print $F[0]' sostituiscono sed e awk in una sola riga. Ma grep, sed e awk sono presenti praticamente su ogni server senza installare nulla, quindi conviene conoscerli per primi.

Conclusioni

grep trova le righe, sed le modifica, awk lavora con le colonne e fa i conteggi. Ricorda il set di base: grep -rniE e -A/-B/-C, sed 's/.../.../g' con -i.bak e -n 'Np', awk -F con $1, $NF, le condizioni e il blocco END. Da questi mattoncini si costruisce quasi ogni analisi dei log. Questi esempi di grep, sed e awk, insieme a find, jq e curl, sono approfonditi in pratica nella lezione del corso Linux dedicata all'elaborazione del testo nel terminale.

Domande frequenti

Qual è la differenza tra grep ed egrep?

egrep è un alias obsoleto di grep -E (espressioni regolari estese). Le versioni recenti di GNU grep mostrano un avviso quando si usa egrep, quindi scrivi grep -E.

Come cerco un testo in tutti i file di una directory su Linux?

Usa grep -rn "testo" /percorso: -r cerca ricorsivamente, -n mostra il numero di riga. Per limitare i tipi di file aggiungi --include="*.php".

Come sostituisco un testo in molti file contemporaneamente?

Combina la ricerca con sed: grep -rl "old" . | xargs sed -i 's/old/new/g'. Prima conviene fare un commit o una copia di backup.

Come stampo più colonne in awk separate da tabulazione?

Imposta il separatore di output: awk -v OFS='\t' '{print $1, $3}' file. Senza OFS i campi stampati vengono separati da uno spazio.

Come elimino gli spazi alla fine delle righe con sed?

Il comando sed -i 's/[[:space:]]*$//' file rimuove spazi e tabulazioni finali da tutte le righe del file.

Impara l’argomento in video

Questo argomento è trattato in modo approfondito, con pratica, nel corso «Linux: с нуля до уверенного администрирования».

Vai al corso