Introduzione

grep è uno dei comandi più utili e potenti in Linux per l'elaborazione dei file. grep cerca in uno o più file di input le righe che corrispondono a un'espressione regolare e scrive ciascuna riga corrispondente nell'output standard.

In questo articolo, esploreremo le basi su come usare le espressioni regolari nella versione GNU di grep, che è disponibile di default nella maggior parte dei sistemi operativi Linux.

Espressione regolare di Grep

Un'espressione regolare o regex è un modello che corrisponde a una serie di stringhe. Un modello è composto da operatori, costruisce caratteri letterali e meta-caratteri, che hanno un significato speciale. GNU grep supporta tre sintassi delle espressioni regolari, Basic, Extended e Perl compatibile.

Nella sua forma più semplice, quando non viene fornito alcun tipo di espressione regolare, grep interpreta i modelli di ricerca come espressioni regolari di base. Per interpretare il modello come un'espressione regolare estesa, utilizzare l'opzione -E (o --extended-regexp).

Nell'implementazione di GNU grep non esiste alcuna differenza funzionale tra la sintassi di base e quella estesa delle espressioni regolari. L'unica differenza è che nelle espressioni regolari di base i meta-caratteri ?, +, {, |, (, e )vengono interpretati come caratteri letterali. Per mantenere i significati speciali dei meta-caratteri quando si usano espressioni regolari di base, i caratteri devono essere preceduti a una barra rovesciata (\). Spiegheremo il significato di questi e altri meta-caratteri in seguito.

Generalmente, dovresti sempre racchiudere l'espressione regolare tra virgolette singole per evitare l'interpretazione e l'espansione dei meta-caratteri da parte della shell.

Ricerca di caratteri letterali (Literal Matches)

L'uso più basilare del comando grep è la ricerca di un carattere letterale o di una serie di caratteri in un file. Ad esempio, per visualizzare tutte le righe contenenti la stringa "bash" nel file /etc/passwd, è necessario eseguire il comando seguente:

grep bash /etc/passwd

L'output dovrebbe assomigliare a questo:

root:x:0:0:root:/root:/bin/bash
noviello:x:1000:1000:noviello:/home/noviello:/bin/bash

In questo esempio, la stringa "bash" è un'espressione regolare di base composta da quattro caratteri letterali. Questo dice a grep di cercare una stringa che ha una "b" immediatamente seguita da "a", "s" e "h".

Per impostazione predefinita, il comando grep fa distinzione tra maiuscole e minuscole. Ciò significa che i caratteri maiuscoli e minuscoli vengono considerati distinti. Per ignorare la distinzione di maiuscole e minuscole durante la ricerca, utilizzare l'opzione -i (o --ignore-case).

È importante notare che grep ricerca il modello di ricerca come una stringa, non una parola. Quindi, se stavi cercando "gnu", grep stamperà anche le righe in cui "gnu" è incorporato in parole più grandi, come "cygnus" o "magnum".

Se la stringa di ricerca include spazi, è necessario racchiuderla tra virgolette singole o doppie:

grep "Gnome Display Manager" /etc/passwd

Ancoraggio (Anchoring)

Le ancore (anchors) sono meta-caratteri che ti consentono di specificare dove nella riga deve essere trovata la corrispondenza.

Il simbolo ^ (cursore) corrisponde alla stringa vuota all'inizio di una riga. Nel seguente esempio, la stringa "linux" corrisponderà solo se si verifica all'inizio di una riga.

grep '^linux' file.txt

Il simbolo $ (dollaro) corrisponde alla stringa vuota all'inizio di una riga. Per trovare una riga che termina con la stringa "linux", dovresti usare:

grep 'linux$' file.txt

Puoi anche costruire un'espressione regolare usando entrambi gli anchors. Ad esempio, per trovare righe contenenti solo "linux", eseguire:

grep '^linux$' file.txt

Un altro esempio utile è il modello ^$ che corrisponde a tutte le linee vuote.

Singolo carattere corrispondente (Matching Single Character)

Il simbolo . (punto) è un meta-carattere che corrisponde a qualsiasi singolo carattere. Ad esempio, per cercare tutto ciò che inizia con "nov" con due caratteri centrali e che termina con la stringa "llo", utilizzare il modello seguente:

grep 'nov..llo' file.txt

Espressioni con parentesi (Bracket Expressions)

Le espressioni di parentesi consentono di abbinare un gruppo di caratteri racchiudendoli tra parentesi []. Ad esempio, per trovare le righe che contengono "accept" o "accent", puoi usare la seguente espressione:

grep 'acce[np]t' file.txt

Se il primo carattere all'interno delle parentesi è il punto di inserimento ^, grep troverà ogni singolo carattere non racchiuso tra parentesi. Il modello seguente corrisponderà a qualsiasi combinazione di stringhe che iniziano con "co" seguito da qualsiasi lettera tranne "l" seguita da "la", come "coca", "cobalt" e così via, ma non corrisponderà alle righe contenenti "cola" “:

grep 'co[^l]a' file.txt

Invece di posizionare i caratteri uno per uno, è possibile specificare un intervallo di caratteri tra parentesi. Un'espressione di intervallo viene costruita specificando il primo e l'ultimo carattere dell'intervallo separati da un trattino. Ad esempio, [a-a] è equivalente a [abcde] e [1-3] è equivalente a [123].

L'espressione seguente corrisponde a ciascuna riga che inizia con una lettera maiuscola:

grep '^[A-Z]' file.txt

grep supporta anche classi predefinite di caratteri racchiuse tra parentesi. La tabella seguente mostra alcune delle classi di caratteri più comuni:

QuantifierCharacter Classes
[:alnum:]Alphanumeric characters.
[:alpha:]Alphabetic characters.
[:blank:]Space and tab.
[:digit:]Digits.
[:lower:]Lowercase letters.
[:upper:]Uppercase letters.

Per un elenco completo di tutte le classi di caratteri, consulta il manuale di Grep.

Quantificatori (Quantifiers)

I quantificatori ti consentono di specificare il numero di occorrenze di elementi che devono essere presenti affinché si verifichi una corrispondenza. La tabella seguente mostra i quantificatori supportati da GNU grep:

QuantifierDescription
*Match the preceding item zero or more times.
?Match the preceding item zero or one time.
+Match the preceding item one or more times.
{n}Match the preceding item exactly n times.
{n,}Match the preceding item at least n times.
{,m}Match the preceding item at most m times.
{n,m}Match the preceding item from n to m times.

Il carattere * (asterisco) corrisponde all'elemento precedente zero o più volte. Il comando seguente troverà  "right", "sright", "ssright" e così via:

grep 's*right'

Di seguito è riportato un modello più avanzato che corrisponde a tutte le righe che iniziano con la lettera maiuscola e terminano con punto o virgola. La regex .* trova qualsiasi numero o qualsiasi carattere:

grep -E '^[A-Z].*[.,]$' file.txt

Il carattere ? (punto interrogativo) rende facoltativo l'elemento precedente e può trovare una sola corrispondenza. Quanto segue corrisponderà sia a "bright" che a "right". Il carattere ? è preceduto da una barra rovesciata (escape) perché stiamo usando espressioni regolari di base:

grep 'b\?right' file.txt

Ecco lo stesso regex usando un'espressione regolare estesa:

grep -E 'b?right' file.txt

Il carattere + (più) corrisponde all'elemento precedente una o più volte. Quanto segue corrisponderà a "sright" e "ssright", ma non "right":

grep -E 's+right' file.txt

I caratteri di parentesi graffa {} consentono di specificare il numero esatto, un limite superiore o inferiore o un intervallo di occorrenze che devono verificarsi affinché si verifichi una corrispondenza.

Quanto segue corrisponde a tutti i numeri interi che hanno tra 3 e 9 cifre:

grep -E '[[:digit:]]{3,9}' file.txt

Alternanza (Alternation)

Il termine alternanza è un semplice "OR". L'operatore di alternanza | (pipe) consente di specificare diverse possibili corrispondenze che possono essere stringhe letterali o set di espressioni. Questo operatore ha la precedenza più bassa di tutti gli operatori di espressioni regolari.

Nell'esempio riportato di seguito, siamo alla ricerca di tutte le occorrenze delle parole fatal, error e critical nel file di errore di Nginx :

grep 'fatal\|error\|critical' /var/log/nginx/error.log

Se si utilizza l'espressione regolare estesa, l'operatore | non deve essere sottoposto a escape, come mostrato di seguito:

grep -E 'fatal|error|critical' /var/log/nginx/error.log

Raggruppamento

Il raggruppamento è una caratteristica delle espressioni regolari che consente di raggruppare i modelli e utilizzarli come un unico elemento di riferimento. I gruppi vengono creati utilizzando la parentesi ().

Quando si usano espressioni regolari di base, la parentesi deve essere preceduta da una barra rovesciata ( \).

L'esempio seguente corrisponde sia a "fearless" che a "less". Il quantificatore ? rende il gruppo (fear) facoltativo:

grep -E '(fear)?less' file.txt

Espressioni speciali di barra rovesciata (Special Backslash Expressions)

GNU grep include diversi meta-caratteri che consistono in una barra rovesciata seguita da un carattere normale. La tabella seguente mostra alcune delle espressioni di barra rovesciata speciali più comuni:

ExpressionDescription
\bMatch a word boundary.
\<Match an empty string at the beginning of a word.
\>Match an empty string at the end of a word.
\wMatch a word.
\sMatch a space.

Il modello seguente corrisponderà a parole separate "abject" e "object". Non corrisponderà alle parole se incorporato in parole più grandi:

grep '\b[ao]bject\b' file.txt

Conclusione

Le espressioni regolari sono utilizzate in editor di testo, linguaggi di programmazione, e gli strumenti da riga di comando, come grep, sed e awk. Sapere come costruire espressioni regolari può essere molto utile durante la ricerca di file di testo, la scrittura di script o il filtraggio dell'output dei comandi.