Linux / Comando grep: cercare testo, regex e righe nei log

grep trova e stampa le righe che corrispondono a un modello nei file o nell’output di altri comandi. Puoi ignorare maiuscole e minuscole, selezionare le righe che non corrispondono, cercare nelle sottodirectory e mostrare il contesto. Per cercare una stringa letterale usa -F; per combinare espressioni regolari usa -E.

La forma di base è grep 'pattern' filename. Poiché il modello può contenere caratteri che hanno significati speciali nella shell, è più sicuro racchiuderlo abitualmente tra apici singoli.

Cos'è il comando grep?

grep legge l'input riga per riga e stampa le righe che corrispondono al modello. Il nome deriva dall'espressione del comando nell'editor di testo Unix ed, g/re/p, che significa trovare e stampare le righe che corrispondono a un'espressione regolare sull'intero intervallo.

Senza file indicati, una ricerca non ricorsiva legge l’ingresso standard: puoi quindi passarle l’output di un altro comando tramite una pipe. Con la ricerca ricorsiva GNU grep usa invece la directory corrente se non specifichi un percorso. La sintassi predefinita è l’espressione regolare di base (Basic Regular Expression, BRE); -E seleziona le espressioni regolari estese (Extended Regular Expression, ERE). Gli esempi seguenti si riferiscono a GNU grep; alcune opzioni differiscono in altre implementazioni.

Sintassi di base e prima ricerca

Questo è uno schema, non una riga da copiare: option, pattern e file sono segnaposto. Le parentesi quadre indicano gli argomenti facoltativi e i puntini consentono più file.

grep [option] 'pattern' [file...]

Per provare il primo comando, usa una nuova directory vuota e verifica che non contenga già app.log o worker.log: il seguente esempio li crea o li sovrascrive. Il piccolo log contiene due righe con ERROR e una riga con WARN.

cat > app.log <<'EOF'
INFO start
ERROR database unavailable
WARN slow response
ERROR retry failed ERROR
INFO completed
EOF
printf '%s\n' 'ERROR worker timeout' > worker.log
grep 'ERROR' app.log

Il risultato del primo comando contiene ERROR database unavailable e ERROR retry failed ERROR, in quest’ordine. Se specifichi entrambi i file, ogni riga selezionata è preceduta dal nome del file; anche worker.log produce una corrispondenza.

grep 'ERROR' app.log worker.log

Gli altri nomi di file e percorsi sono esempi da adattare: config.txt, app.conf, access.log, contacts.txt e i percorsi /etc richiedono file realmente esistenti e leggibili. Prova le ricerche ricorsive soltanto su una directory di esercizio o su un ambito scelto consapevolmente; non è necessario creare o modificare configurazioni di sistema.

Su Linux puoi filtrare i messaggi del kernel leggibili con dmesg. Il comando seguente cerca usb senza distinguere maiuscole e minuscole. L’accesso può essere limitato dalla politica del sistema: un errore di permesso non significa che non esistano messaggi USB; non modificare la politica per eseguire questa prova.

dmesg | grep -i 'usb'

Opzioni comuni di grep

Opzione Funzione Esempi di utilizzo
-i Cerca senza distinzione tra maiuscole e minuscole grep -i 'error' app.log
-v Seleziona le righe che non corrispondono grep -v '^#' config
-n Mostra i numeri delle righe nel risultato grep -n 'TODO' main.c
-w Cerca il modello come una parola intera grep -w 'cat' words.txt
-x Seleziona le righe che corrispondono interamente al modello grep -x 'enabled' status.txt
-F Considera il pattern come una stringa fissa, non come un'espressione regolare grep -F 'a.b' data.txt
-E Usa espressioni regolari estese grep -E 'error|failed' app.log
-r Cerca ricorsivamente nelle sottodirectory grep -r 'listen' /etc/nginx
-l Mostra solo i nomi dei file con corrispondenze grep -rl 'deprecated' src
-c Conta le righe selezionate per ciascun file grep -c 'ERROR' app.log
-A, -B, -C Mostra il contesto dopo, prima o intorno alle righe selezionate grep -C 2 'panic' app.log

Le opzioni sopra possono essere combinate. Ad esempio, grep -in 'error' app.log cercherà ignorando le maiuscole e minuscole e mostrerà anche i numeri di riga. Il comportamento dettagliato e le opzioni estese di GNU possono essere trovati nella documentazione ufficiale di GNU Grep.

Distinguere tra stringhe ed espressioni regolari

Cercare stringhe fisse con -F

I caratteri che hanno significati speciali nelle espressioni regolari, come punti, asterischi e parentesi, possono essere ricercati letteralmente usando -F. Nel comando seguente, il punto è trattato come un vero punto anziché come qualsiasi carattere.

grep -F '192.168.0.1' access.log

Con -F i punti sono letterali, ma la ricerca rimane per sottostringa: anche 192.168.0.10 contiene 192.168.0.1. Per un campo IP esatto devi delimitare il campo in base al formato reale del log; -xF è adatto solo se l’intera riga contiene quel valore.

Usare espressioni regolari estese con -E

L'uso di -E ti consente di utilizzare la sintassi delle espressioni regolari estese, come |, +, ?, e le parentesi, in modo più leggibile. L'esempio seguente trova le righe che contengono uno tra ERROR, WARN o FATAL.

grep -E 'ERROR|WARN|FATAL' app.log

In GNU grep, la differenza tra espressioni regolari di base ed estese risiede principalmente nella notazione. Se la compatibilità con altri ambienti è importante, controlla l'implementazione di grep e le opzioni supportate dal sistema di destinazione.

Specificare l’inizio e la fine di una riga

Il caret (^) rappresenta l'inizio di una riga, e il simbolo del dollaro ($) rappresenta la fine di una riga. Una riga vuota può essere trovata con ^$, che non ha caratteri tra l'inizio e la fine.

grep '^server' config.txt
grep 'completed$' job.log
grep '^$' notes.txt

Per escludere righe vuote e righe di commento da un file di configurazione, puoi applicare le due condizioni in sequenza come segue.

grep -v '^[[:space:]]*#' app.conf | grep -v '^[[:space:]]*$'

Restringere l’ambito della ricerca

Corrispondenza dell'intera parola e dell'intera riga

-w richiede che la corrispondenza sia delimitata da inizio/fine riga o da caratteri non appartenenti a una parola. In GNU grep lettere, cifre e underscore appartengono alle parole, con dettagli che dipendono dal locale: cat corrisponde in cat e cat-dog, ma non in category o cat_dog.

grep -w 'root' /etc/passwd

La ricerca di root in /etc/passwd non è limitata al primo campo: può selezionare root anche in altri campi. Per cercare il nome di login root all’inizio della riga usa grep '^root:' /etc/passwd.

-x seleziona una riga solo quando l'intera riga corrisponde esattamente al modello.

grep -x 'active' service-status.txt

Trovare righe che non corrispondono

-v inverte la selezione. L'esempio seguente stampa solo le righe che non contengono DEBUG.

grep -v 'DEBUG' app.log

Quando combinato con -i, puoi escludere senza considerare le maiuscole e le minuscole.

grep -iv 'debug' app.log

Ricerca di più modelli contemporaneamente

Usare -e più volte permette di specificare ogni modello separatamente. Non è necessario combinarli in un'unica espressione regolare complessa, rendendo più facile la lettura negli script.

grep -e 'ERROR' -e 'FATAL' app.log

Con -f, crea patterns.txt con un modello per riga, per esempio ERROR e FATAL su due righe distinte. I modelli sono alternative: basta che ne corrisponda uno. Una riga vuota è un modello vuoto e seleziona tutte le righe; un file completamente vuoto non contiene modelli e non seleziona nulla.

grep -f patterns.txt app.log

Cercare ricorsivamente in una directory

Differenza tra -r e -R

-r cerca ricorsivamente i file nella directory specificata. Segue i collegamenti simbolici specificati direttamente nella riga di comando, ma ignora i collegamenti simbolici incontrati durante le ricerche ricorsive.

grep -rn 'database_url' ./config

-R di GNU grep segue anche i collegamenti simbolici incontrati durante la visita. Non crea collegamenti: può attraversare un ciclo già presente o raggiungere percorsi esterni alla directory prevista. Usa questa opzione solo quando vuoi seguire tali destinazioni.

Inclusione ed esclusione dei tipi di file

--include limita la ricerca ai nomi di file che corrispondono al modello; --exclude salta i nomi corrispondenti. Sono modelli di nomi, non espressioni regolari. Racchiudili tra apici per evitare che la shell espanda gli asterischi prima di grep.

grep -rn --include='*.conf' 'timeout' /etc
grep -rn --exclude='*.min.js' 'deprecated' ./src

Per escludere directory specifiche, usa --exclude-dir.

grep -rn --exclude-dir='.git' --exclude-dir='node_modules' 'api_key' .

Mostrare solo i nomi dei file

Se hai bisogno di un elenco di file piuttosto che del contenuto corrispondente, usa -l. Ogni file smette di essere cercato dopo il primo match trovato, il che si adatta bene anche allo scopo.

grep -rl --include='*.html' 'jb-heading' .

Al contrario, per trovare solo file senza modelli corrispondenti, usa -L maiuscolo.

grep -rL --include='*.html' 'jb-heading' .

Leggere i risultati della ricerca

Mostrare numeri di riga e nomi dei file

-n mostra i numeri delle righe corrispondenti. Per mostrare anche il nome del file quando ne cerchi uno solo, usa -H di GNU grep; per nasconderlo, usa -h.

grep -nH 'listen' nginx.conf

Visualizzare le righe prima e dopo una corrispondenza

Se è difficile determinare la causa solo dalle linee di errore, utilizzare -A, -B, o -C per visualizzare le linee circostanti. Il numero indica quante linee aggiuntive vengono stampate.

grep -A 3 'ERROR' app.log
grep -B 2 'ERROR' app.log
grep -C 2 'ERROR' app.log

-A 3 aggiunge 3 linee dopo, -B 2 aggiunge 2 linee prima, e -C 2 aggiunge 2 linee prima e dopo.

Stampare solo le parti corrispondenti

-o mostra ogni parte corrispondente non vuota su una riga distinta. Le corrispondenze sono non sovrapposte: non conta tutte le possibili sovrapposizioni. Nell’esempio, contacts.txt deve contenere testo da cui estrarre sequenze di tre cifre, un trattino e quattro cifre; non è una convalida completa di numeri telefonici.

grep -oE '[0-9]{3}-[0-9]{4}' contacts.txt

Per evidenziare le corrispondenze nel terminale, puoi usare --color=auto. Quando invii l'output a un file o a un altro comando, usa auto o never per evitare che i caratteri di controllo del colore vengano mescolati.

Filtrare log e output dei comandi

Trovare errori nei log recenti

Per cercare solo l'intervallo recente invece dell'intero log, collegalo con tail.

tail -n 500 app.log | grep -iE 'error|failed|fatal'

La pipeline seguente mostra inizialmente le ultime dieci righe del file filtrate da grep e poi continua con le righe aggiunte. Per seguire soltanto le nuove aggiunte, usa tail -n 0 -F app.log al posto del primo comando. In primo piano, Ctrl+C interrompe il monitoraggio.

tail -F app.log | grep --line-buffered 'ERROR'

--line-buffered è un’opzione GNU che usa un buffer per riga per l’uscita di grep; non costringe il programma che produce i log a svuotare i propri buffer. Il comportamento di tail -F durante la rotazione è spiegato nella guida al comando tail di Linux.

Filtrare servizi e processi

Sui sistemi che usano systemd, systemctl list-unit-files elenca i file di unità e il loro stato di abilitazione, non soltanto i servizi attualmente in esecuzione. Il filtro seguente trova nginx come sottostringa delle righe; per l’uso negli script puoi aggiungere --no-pager a systemctl.

systemctl list-unit-files | grep 'nginx'

Per trovare processi, il manuale procps di pgrep distingue -f, che cerca nell’intera riga di comando, da -a, che la mostra insieme al PID (identificativo del processo). Il modello è un’espressione regolare: controlla comando e argomenti dei risultati, perché una corrispondenza non prova che il servizio funzioni correttamente.

pgrep -af 'nginx'

Contare le righe corrispondenti

-c stampa il numero di righe corrispondenti, non il numero di corrispondenze. Anche se una riga contiene il modello più volte, quella riga viene conteggiata solo una volta.

grep -c 'ERROR' app.log

Per contare le corrispondenze non vuote e non sovrapposte, separale con -o e passa l’output a wc -l. Nel piccolo app.log iniziale grep -c restituisce 2, mentre la pipeline seguente restituisce 3: la seconda riga selezionata contiene ERROR due volte.

grep -o 'ERROR' app.log | wc -l

Verifica dell'esistenza senza output

-q sopprime l’output standard e permette di controllare l’esistenza di una corrispondenza in una condizione. Per provare l’esempio, crea un file status.log con una riga ready: verrà stampato Servizio pronto. Anche not ready contiene la sottostringa ready; per una riga esattamente uguale usa -qx 'ready'. Un risultato diverso da zero fa saltare then sia in caso di assenza sia in caso di errore: questa semplice condizione non distingue i due casi.

if grep -q 'ready' status.log; then
 echo 'Servizio pronto'
fi

Comprendere lo stato di uscita di grep

grep comunica i risultati della ricerca non solo tramite la stringa di output, ma anche attraverso lo stato di uscita. Tipicamente, restituisce 0 se ci sono righe corrispondenti, 1 se non ci sono righe corrispondenti, e 2 se non può leggere un file o c'è un problema di sintassi.

Stato di uscita Significato Decisione negli script
0 Una o più corrispondenze trovate Elaborare come se la condizione fosse vera
1 Nessuna corrispondenza trovata Potrebbe essere un normale risultato della ricerca
2 Si è verificato un errore Controllare il file di input, i permessi o il modello

La mancata corrispondenza è un esito di ricerca distinto da un errore: conserva subito $? se devi analizzarlo. Lo stato di una pipeline Bash è normalmente quello dell’ultimo comando, quindi un errore di dmesg, tail o grep può essere nascosto da un comando successivo. Valuta pipefail o PIPESTATUS quando devi verificare ogni fase, considerando anche l’uscita anticipata di grep -q. GNU grep -q può restituire 0 dopo una corrispondenza anche se un altro file ha dato errore; per rilevare tutti gli errori di lettura evita l’arresto anticipato e controlla stderr e stato finale.

Problemi comuni e soluzioni

Quando il modello inizia con un trattino

I modelli -ERROR come questo possono essere scambiati per opzioni. Metti il modello dopo --, che indica la fine delle opzioni.

grep -- '-ERROR' app.log

Quando appare il messaggio 'Binary file matches'

Se GNU grep determina che l'input è un file binario, potrebbe mostrare il messaggio invece delle righe corrispondenti. Se sei sicuro che sia effettivamente un file di testo, puoi trattarlo come testo usando -a.

grep -a 'ERROR' mixed-data.log

Al contrario, per escludere i file binari nelle ricerche ricorsive, puoi usare -I. Non usare sempre -a su dati binari sconosciuti, poiché potrebbero contenere caratteri di controllo che possono compromettere il tuo terminale.

Quando i risultati differiscono a causa dei caratteri speciali delle regex

Il punto corrisponde a qualsiasi singolo carattere e l'asterisco indica la ripetizione dell'espressione precedente nelle espressioni regolari. Se intendevi cercare una stringa semplice, usa -F, e se intendevi usare un'espressione regolare, racchiudi l'intero modello tra virgolette singole per evitare l'espansione della shell.

Quando le ricerche ricorsive sono troppo lente o producono troppi risultati

Restringi la directory di partenza per la ricerca ed escludi i file non necessari usando --include, --exclude e --exclude-dir. Per nascondere solo gli errori di permesso, potresti essere tentato di scartare tutti gli errori standard, ma farlo può farti perdere problemi importanti. Pertanto, è meglio controllare prima l'ambito della ricerca e i permessi dei file.

Domande frequenti

Per impostazione predefinita, grep distingue tra lettere maiuscole e minuscole?

Sì. La ricerca predefinita distingue tra maiuscole e minuscole. Per ignorare le maiuscole/minuscole, usa -i. Dovresti anche considerare che i risultati della gestione delle maiuscole/minuscole possono variare in base alle impostazioni locali (locale).

Qual è la differenza tra grep -r e grep -R?

In GNU grep, entrambi eseguono ricerche ricorsive, ma -R segue tutti i link simbolici incontrati durante la ricerca. Per le ricerche tipiche di codice sorgente o file di configurazione, -r è più facile da prevedere.

È un errore se non vengono trovati risultati della ricerca?

Non necessariamente. Se non ci sono corrispondenze, di solito restituisce uno stato di uscita di 1, mentre errori come il mancato accesso a un file o opzioni non valide sono distinti dallo stato 2.

Come faccio a trovare righe che contengono almeno uno di diversi termini di ricerca?

grep -E 'error|warning' filename utilizza l'operatore di alternanza nelle espressioni regolari estese, oppure puoi ripetere -e come grep -e 'error' -e 'warning' filename. Per trattare il termine di ricerca come una stringa semplice piuttosto che come un'espressione regolare, puoi anche usare -F.

Riepilogo

Per le ricerche generali nelle stringhe, è utile familiarizzare con grep -F; per condizioni multiple con espressioni regolari, grep -E; e per cercare in un'intera directory, grep -r. Per rendere i risultati più facili da interpretare, puoi usare -n e -C; se ti serve solo l'elenco dei file, utilizza -l; e per verificare l'esistenza negli script, usa -q.

Racchiudi i pattern tra virgolette singole, limita specificamente l'ambito delle ricerche ricorsive, e distingui tra stato di uscita 1 e stato di errore 2—questi sono elementi chiave per un utilizzo sicuro. Per esplorare insieme altri strumenti di base, consulta l’elenco dei comandi Linux più usati.