Gli atomi: caratteri, ancore, parentesi quadre
Un’espressione regolare è una sequenza di atomi. La maggior parte dei caratteri vale per sé stessa; alcuni hanno un significato speciale. Il punto . corrisponde a un carattere qualsiasi, quindi .c si trova sia in abcd sia in a1cd, mentre bc solo nella prima. Il circonflesso ^ àncora l’inizio della riga e il dollaro $ la fine: cd si trova in abcd ma ^cd no, e ab$ non si trova in abcd. Altrove sono caratteri ordinari.
Le parentesi quadre delimitano un unico atomo che corrisponde a uno qualunque dei caratteri elencati: [1b] si trova in entrambe le stringhe di prima. Con ^ subito dopo la quadra aperta il senso si rovescia, e [^1b] corrisponde a qualsiasi carattere tranne quei due. Dentro le quadre puoi scrivere intervalli come [0-9] o [a-z], ma gli intervalli dipendono dal locale, quindi su un sistema configurato in modo insolito [a-z] può comportarsi in modo inatteso.
Più solide sono le classi POSIX: [:alpha:], [:digit:], [:alnum:], [:upper:], [:lower:], [:space:], [:blank:], [:punct:], [:xdigit:]. Esistono solo dentro una bracket expression, quindi le quadre sono sempre due:
grep '[[:digit:]]' file.txt corretto
grep '[:digit:]' file.txt sbagliato: cerca i caratteri : d g i t
È l’errore più frequente dell’obiettivo, e in una domanda a completamento non c’è nessuno a correggerti.
BRE ed ERE: la stessa cosa scritta in due modi
POSIX definisce due dialetti: espressioni regolari di base (BRE) ed estese (ERE). Non cambiano le funzioni, cambia chi ha bisogno del backslash: in BRE i metacaratteri avanzati sono letterali e vanno protetti, in ERE sono attivi di default.
ERE BRE
zero o più a* a*
uno o più a+ a\+
zero o uno a? a\?
esattamente 3 a{3} a\{3\}
almeno 2 a{2,} a\{2,\}
da 2 a 4 a{2,4} a\{2,4\}
alternativa he|him he\|him
raggruppamento (ab)+ \(ab\)\+
back reference \1 \1
L’asterisco è l’unico quantificatore identico nei due dialetti. Quantificatori e limiti si applicano solo all’atomo che li precede: un limite da 2 a 4 scritto dopo xyz riguarda la sola z, non l’intera sequenza. E quando due corrispondenze partono dallo stesso punto vince la più lunga.
Le forme BRE con backslash per il più, il punto interrogativo e l’alternativa sono estensioni GNU: su Linux funzionano ovunque, ma il codice portabile usa ERE.
Chi parla quale dialetto: grep e sed di default usano BRE; grep -E (e il vecchio egrep) e sed -E usano ERE; grep -F (e il vecchio fgrep) non interpreta nulla e cerca la stringa letterale, comodo quando il testo contiene punti o asterischi veri. egrep e fgrep sono ancora nel programma d’esame ma da GNU grep 3.8 stampano un avviso di obsolescenza. La pagina di riferimento è man 7 regex.
grep: le opzioni che vengono chieste
$ grep -v '^#' /etc/services righe che NON iniziano con #
$ grep -c '^options' alsa-base.conf conta le righe corrispondenti
$ grep -n -i errore /var/log/messages numero di riga, senza distinguere maiuscole
$ grep -r -l 'Listen' /etc elenca solo i file che contengono il testo
$ grep -E -H -C 1 '3d (modeling|printing)' guida.md
Il file di log dell’esempio cambia famiglia: su Red Hat e Fedora è /var/log/messages, su Debian e Ubuntu è /var/log/syslog.
Da tenere a mente: -i ignora maiuscole e minuscole, -v inverte la selezione, -c conta, -n numera, -l mostra solo i nomi dei file, -o solo la porzione corrispondente, -r scende ricorsivamente, -w impone la parola intera, -f FILE legge i pattern da un file, -A, -B e -C aggiungono righe di contesto. L’opzione -H merita un avviso: il nome del file compare da solo quando i file sono più di uno, ma non con un file singolo, quindi con find -exec grep va aggiunta a mano. Con find -print0 serve invece -z.
sed: indirizzi, cancellazioni, sostituzioni
sed è un editor di flusso: non mostra nulla sullo schermo, applica istruzioni riga per riga. Un’istruzione è una lettera, eventualmente preceduta da un indirizzo, che può essere un numero di riga, un intervallo separato da virgola, il dollaro per l’ultima riga o un’espressione regolare fra due barre.
$ sed '1d' elenco.txt cancella la prima riga
$ sed '1,7d' elenco.txt cancella dalla prima alla settima
$ sed '/^#/d' /etc/services cancella le righe di commento
$ sed -n '/^options/p' modulo.conf stampa SOLO le righe corrispondenti
$ sed -e '1,7d' -e '11d' elenco.txt due istruzioni, due -e
$ sed -f script.sed elenco.txt istruzioni lette da file
L’opzione -n spegne la stampa automatica di ogni riga; da lì in poi stampa solo ciò che chiedi con p. È la coppia -n più p che rende sed un sostituto di grep.
L’istruzione più usata resta la sostituzione:
$ sed 's/hda/sda/' fstab.bak solo la PRIMA occorrenza di ogni riga
$ sed 's/hda/sda/g' fstab.bak tutte le occorrenze
$ sed -i.bak 's/hda/sda/g' fstab modifica il file sul posto, con backup
$ sed 's|/usr/local|/opt|g' lista separatore diverso dalla barra
$ sed -e 's/.* \(.*\)$/\1/' lista tiene solo l'ultimo campo della riga
Nel testo di sostituzione il carattere & rappresenta tutto ciò che ha corrisposto, mentre \1 e \2 richiamano i gruppi catturati, che in BRE si scrivono con \( e \). Ricorda che -i e -E sono estensioni GNU e che -i riscrive il file senza possibilità di annullare: provalo prima senza, poi passagli un suffisso di backup.