grep, sed e awk: esempi di elaborazione del testo nel terminale Linux
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.
| Compito | Strumento | Esempio |
|---|---|---|
| Trovare righe con un testo | grep | grep ERROR app.log |
| Sostituire un testo | sed | sed 's/http:/https:/g' file |
| Eliminare o mostrare righe per numero | sed | sed -n '10,20p' file |
| Estrarre una colonna | awk | awk '{print $1}' access.log |
| Calcolare somma, media, raggruppamenti | awk | awk '{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.
| Costrutto | Significato | Esempio |
|---|---|---|
^ | inizio della riga | grep '^root' /etc/passwd |
$ | fine della riga | grep 'bash$' /etc/passwd |
. | qualsiasi carattere | grep 'h.t' |
* | da 0 ripetizioni in su | grep 'ab*c' |
+ (ERE) | 1 o più | grep -E '[0-9]+' |
| (ERE) | oppure | grep -E 'error|warn' |
[...] | un carattere dall'insieme | grep '[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$1prima che awk parta. Scrivi il programma awk e le espressioni sed tra virgolette singole. - Dimenticare
gin sed. Senza di esso viene sostituita solo la prima corrispondenza nella riga, le altre restano. sed -isenza verifica. Un'espressione regolare sbagliata rovina il file in silenzio. Prima esegui senza-ioppure usa-i.bak.+e|non funzionano in grep. È sintassi estesa: servegrep -E(osed -E).- Il punto nel modello.
grep 192.168.1.1trova anche192x168y1z1: il punto significa «qualsiasi carattere». Usagrep -Foppure scappa il punto con\.. - Spazi nelle colonne. Se un campo può contenere spazi (CSV, percorsi), imposta il separatore in modo esplicito:
awk -F,oppureawk -F'\t'. - Cat inutile.
cat file | grep xfunziona, magrep 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.
Questo argomento è trattato in modo approfondito, con pratica, nel corso «Linux: с нуля до уверенного администрирования».
Vai al corso