GNU/Linux >> Linux Esercitazione >  >> Linux

Regex e grep:flusso di dati e blocchi predefiniti

In Introduzione alle espressioni regolari , ho spiegato cosa sono e perché sono utili. Ora diamo uno sguardo più approfondito a come vengono creati. Perché GNU grep è uno degli strumenti che utilizzo di più (che fornisce un'implementazione più o meno standardizzata delle espressioni regolari), userò quel set di espressioni come base per questo articolo. Quindi esamineremo sed (un altro strumento che utilizza espressioni regolari) in un articolo successivo.

Tutte le implementazioni di espressioni regolari sono basate su righe. Un modello creato da una combinazione di una o più espressioni viene confrontato con ciascuna riga di un flusso di dati. Quando viene effettuata una corrispondenza, viene eseguita un'azione su quella linea come prescritto dallo strumento utilizzato.

Ad esempio, quando si verifica una corrispondenza di pattern con grep , l'azione usuale consiste nel passare quella riga a STDOUT e scartare le righe che non corrispondono al modello. Come abbiamo visto in Iniziare con le espressioni regolari:un esempio , il -v l'opzione inverte queste azioni, in modo che le righe con le corrispondenze vengano scartate.

Ogni riga del flusso di dati viene valutata da sola. Pensa a ciascuna linea di flusso di dati come a un record, in cui gli strumenti che utilizzano le espressioni regolari elaborano un record alla volta. Quando viene effettuata una corrispondenza, viene eseguita un'azione definita dallo strumento in uso sulla riga che contiene la stringa corrispondente.

Mattoni regex

La tabella seguente contiene un elenco delle espressioni e dei metacaratteri dei blocchi predefiniti implementati da GNU grep comando (e la maggior parte delle altre implementazioni regex) e le loro descrizioni. Quando viene utilizzata in un modello, ciascuna di queste espressioni o metacaratteri corrisponde a un singolo carattere nel flusso di dati analizzato:

Espressione Descrizione

Caratteri alfanumerici

Letterali

A-Z,a-z,0-9

Tutti i caratteri alfanumerici e alcuni segni di punteggiatura sono considerati letterali. Quindi la lettera a in una regex corrisponderà sempre alla lettera "a" nel flusso di dati da analizzare. Non c'è ambiguità per questi personaggi. Ogni carattere letterale corrisponde a uno e un solo carattere.
. (punto) Il metacarattere punto (.) è la forma di espressione più elementare. Corrisponde a qualsiasi singolo carattere nella posizione in cui si incontra in un pattern. Quindi il modello b.g corrisponderebbe a "grande", "più grande", "borsa", "baguette" e "palude", ma non a "cane", "blog", "abbraccio", "ritardo", "bavaglio", "gamba" ecc. .

Espressione tra parentesi

[elenco di caratteri]

GNU grep la chiama un'espressione tra parentesi ed è la stessa di un set per la shell Bash. Le parentesi racchiudono un elenco di caratteri da abbinare per la posizione di un singolo carattere nel modello. [abcdABCD] corrisponde alle lettere "a", b", "c" o "d" in maiuscolo o minuscolo. [a-dA-D] specifica un intervallo di caratteri che crea la stessa corrispondenza. [a-zA-Z] corrisponde all'alfabeto in maiuscolo e minuscolo.

[:nome classe:]

Classi di personaggi

Questo è un tentativo POSIX di standardizzazione delle espressioni regolari. I nomi delle classi dovrebbero essere ovvi. Ad esempio, il [:alnum:] class corrisponde a tutti i caratteri alfanumerici. Altre classi sono [:digit :] che corrisponde a qualsiasi cifra 0-9, [:alpha:] ,[:space:] , e così via. Si noti che potrebbero esserci problemi dovuti alle differenze nelle sequenze di ordinamento in diverse località. Leggi il grep pagina man per i dettagli.

^ e $

Ancore

Questi due metacaratteri corrispondono rispettivamente all'inizio e alla fine di una riga. Si dice che ancorano il resto del modello all'inizio o alla fine di una riga. L'espressione ^b.g corrisponderebbero solo a "grande", "più grande", "borsa" ecc., come mostrato sopra se si verificano all'inizio della riga da analizzare. Il modello b.g$ corrisponderebbero a "big" o "bag" solo se si trovano alla fine della riga, ma non a "big".

Esaminiamo questi elementi costitutivi prima di continuare con alcuni dei modificatori. Il file di testo che useremo per l'Esperimento 3 proviene da un progetto di laboratorio che ho creato per una vecchia classe Linux che insegnavo. Originariamente era in un file odt di LibreOffice Writer, ma l'ho salvato in un file di testo ASCII. La maggior parte della formattazione di cose come le tabelle è stata rimossa, ma il risultato è un lungo file di testo ASCII che possiamo usare per questa serie di esperimenti.

Esempio:voci del sommario

Diamo un'occhiata a un esempio per esplorare ciò che abbiamo appena imparato. Per prima cosa, crea il ~/testing directory della tua PWD (creala se non l'hai già fatto nell'articolo precedente di questa serie), quindi scarica il file di esempio da GitHub.

[student@studentvm1 testing]$  wget https://raw.githubusercontent.com/opensourceway/reg-ex-examples/master/Experiment_6-3.txt

Per iniziare, usa il less comando per guardare ed esplorare Experiment_6-3.txt file per qualche minuto per avere un'idea del suo contenuto.

Ora, usiamo un semplice grep espressioni per estrarre righe dal flusso di dati di input. Il sommario (TOC) contiene un elenco di progetti e i rispettivi numeri di pagina nel documento PDF. Estraiamo il TOC iniziando con le righe che terminano con due cifre:

[student@studentvm1 testing]$  grep [0-9][0-9]$ Experiment_6-3.txt

Questo comando non è proprio quello che vogliamo. Visualizza tutte le righe che terminano con due cifre e mancano le voci del sommario con una sola cifra. Vedremo come gestire un'espressione per una o più cifre in un esperimento successivo. Guardando l'intero file in less , potremmo fare qualcosa del genere.

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[0-9]$"

Questo comando è molto più vicino a quello che vogliamo, ma non è proprio lì. Otteniamo alcune righe da più avanti nel documento che corrispondono anche a queste espressioni. Se studi le righe extra e guardi quelle nel documento completo, puoi capire perché corrispondono pur non facendo parte del sommario.

In questo comando mancano anche le voci di sommario che non iniziano con "Progetto di laboratorio". A volte questo risultato è il meglio che puoi fare e dà una visione migliore del TOC rispetto a prima. Vedremo come combinare questi due grep istanze in una singola in un esperimento successivo.

Ora modifichiamo un po' questo comando e usiamo l'espressione POSIX. Nota le doppie parentesi quadre ([[]] ) intorno ad esso:

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[[:digit:]]$"

Le parentesi graffe generano un messaggio di errore.

Questo comando dà gli stessi risultati del tentativo precedente.

Esempio:systemd

Cerchiamo qualcosa di diverso nello stesso file:

[student@studentvm1 testing]$ grep systemd Experiment_6-3.txt

Questo comando elenca tutte le occorrenze di "systemd" nel file. Prova a usare -i opzione per assicurarti di ottenere tutte le istanze, comprese quelle che iniziano con lettere maiuscole (la forma ufficiale di "systemd" è tutta minuscola). Oppure puoi cambiare l'espressione letterale in Systemd .

Conta il numero di righe che contengono la stringa systemd . Uso sempre -i per garantire che tutte le istanze dell'espressione di ricerca vengano trovate indipendentemente dal caso:

[student@studentvm1 testing]$ grep -i systemd Experiment_6-3.txt | wc
20      478     3098

Come puoi vedere, ho 20 righe e dovresti avere lo stesso numero.

Esempio:metacaratteri

Ecco un esempio di corrispondenza di un metacarattere:la parentesi a sinistra ([ ). Per prima cosa, proviamo senza fare nulla di speciale:

[student@studentvm1 testing]$  **grep -i "[" Experiment_6-3.txt**
grep: Invalid regular expression

Questo errore si verifica perché [ viene interpretato come un metacarattere. Dobbiamo scappare questo carattere con una barra rovesciata (\ ) in modo che sia interpretato come un carattere letterale e non come un metacarattere:

[student@studentvm1 testing]$ grep -i "\[" Experiment_6-3.txt

La maggior parte dei metacaratteri perdono il loro significato speciale se usati all'interno di espressioni tra parentesi:

  • Per includere un ] letterale , inseriscilo per primo nell'elenco.
  • Per includere un ^ letterale , posizionalo ovunque tranne che per primo.
  • Per includere un [ letterale , posizionalo per ultimo.

Ripetizione

Le espressioni regolari possono essere modificate utilizzando operatori che consentono di specificare zero, una o più ripetizioni di un carattere o di un'espressione. Questi operatori di ripetizione vengono inseriti immediatamente dopo il carattere letterale o il metacarattere utilizzato nel modello:

Operatore Descrizione
?

Nelle espressioni regolari il ? significa zero o un'occorrenza al massimo del carattere precedente. Quindi, ad esempio, drives? corrisponde a "drive" e "drives" ma non a "driver". Questo risultato è leggermente diverso dal comportamento di ? in un globo.

* Il carattere che precede il * sarà abbinato zero o più volte senza limite. In questo esempio, drives* corrisponde a "drive", "drives" e "drivesss" ma non a "driver". Di nuovo, questo è un po' diverso dal comportamento di * in un globo.
+ Il carattere che precede il + verranno abbinati una o più volte. Il carattere deve esistere nella riga almeno una volta affinché si verifichi una corrispondenza. Ad esempio, drives+ corrisponde a "drives" e "drivesss" ma non a "drive" o "driver".
{n} Questo operatore corrisponde esattamente al carattere precedente n volte. L'espressione drives{2} corrisponde a "drivess" ma non a "drive", "drives", "drivesss" o qualsiasi numero di caratteri "s" finali. Tuttavia, poiché "drivesssss" contiene la stringa drivess , si verifica una corrispondenza su quella stringa, quindi la riga sarebbe una corrispondenza di grep .
{n,} Questo operatore corrisponde al carattere precedente n o più volte. L'espressione drives{2,} corrisponde a "drivess" ma non a "drive", "drives", "drivess", "drives" o qualsiasi numero di caratteri "s" finali. Perché "drivesssss" contiene la stringa drivess , si verifica una corrispondenza.
{,m} Questo operatore corrisponde al carattere precedente non più di m volte. L'espressione drives{,2} corrisponde a "drive", "drives" e "drivess", ma non "drivesss" o qualsiasi numero di caratteri "s" finali. Ancora una volta, perché "drivesssss" contiene la stringa drivess , si verifica una corrispondenza.
{n,m} Questo operatore corrisponde al carattere precedente almeno n volte, ma non più di m volte. L'espressione drives{1,3} corrisponde a "drives", "drivess" e "drivesss", ma non "drivessss" o un numero qualsiasi di "s" finali. Ancora una volta, poiché "drivesssss" contiene una stringa corrispondente, si verifica una corrispondenza.

Ad esempio, esegui ciascuno dei seguenti comandi ed esamina attentamente i risultati, in modo da capire cosa sta succedendo:

[student@studentvm1 testing]$  **grep -E files? Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives*" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives+" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{,2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,3}" Experiment_6-3.txt**

Assicurati di sperimentare questi modificatori su altro testo nel file di esempio.

Modificatori di metacaratteri

Ci sono ancora alcuni modificatori interessanti e importanti che dobbiamo esplorare:

Modificatore Descrizione
< Questa espressione speciale corrisponde alla stringa vuota all'inizio di una parola. L'espressione <fun corrisponderebbe a "divertimento" e "Funzione", ma non a "rimborso".
> Questa espressione speciale corrisponde allo spazio normale o alla stringa vuota (" ") alla fine di una parola, nonché alla punteggiatura che in genere appare nella stringa di un carattere alla fine di una parola. Quindi environment> corrisponde a "ambiente", "ambiente" e "ambiente", ma non "ambienti" o "ambientali".
^ In un'espressione di classe di caratteri, questo operatore nega l'elenco di caratteri. Pertanto, mentre la classe [a-c] corrisponde a "a", "b" o "c" in quella posizione del pattern, la classe [^a-c] corrisponde a qualsiasi cosa tranne "a", "b" o "c."
| Se utilizzato in una regex, il | metacharacter è un operatore logico "o". Si chiama ufficialmente infix o alternanza operatore. Abbiamo già incontrato questo in Iniziare con le espressioni regolari:un esempio , dove abbiamo visto che la regex "Team|^\s*$" significa "una riga con 'Team' o (| ) una riga vuota con zero, uno o più spazi bianchi come spazi, tabulazioni e altri caratteri non stampabili."
( and ) Le parentesi ( and ) ci consentono di garantire una sequenza specifica di confronto dei modelli, come potrebbe essere utilizzata per i confronti logici in un linguaggio di programmazione.

Ora abbiamo un modo per specificare i limiti delle parole con il \< e \> metacaratteri. Ciò significa che ora possiamo essere ancora più espliciti con i nostri schemi. Possiamo anche usare la logica in schemi più complessi.

Ad esempio, inizia con un paio di schemi semplici. Questo primo seleziona tutte le istanze di drives ma non drive , drivess o caratteri "s" finali aggiuntivi:

 [student@studentvm1 testing]$  **grep -Ei "\<drives\>" Experiment_6-3.txt**

Ora costruiamo un modello di ricerca per individuare i riferimenti a tar (il comando di archiviazione nastro) e relativi riferimenti. Le prime due iterazioni mostrano più di un semplice tar -righe correlate:

[student@studentvm1 testing]$ grep -Ei "tar" Experiment_6-3.txt
[student@studentvm1 testing]$ grep -Ei "\<tar" Experiment_6-3.txt
[student@studentvm1 testing]$  grep -Ein "\<tar\>" Experiment_6-3.txt

Il -n l'opzione nell'ultimo comando sopra mostra i numeri di riga per ciascuna riga in cui si è verificata una corrispondenza. Questa opzione può aiutare a individuare istanze specifiche del modello di ricerca.

Suggerimento: Le righe di dati corrispondenti possono estendersi oltre una singola schermata, soprattutto durante la ricerca di un file di grandi dimensioni. È possibile reindirizzare il flusso di dati risultante tramite l'utilità less e quindi utilizzare la funzione di ricerca less che implementa anche le espressioni regolari per evidenziare le occorrenze delle corrispondenze nel modello di ricerca. L'argomento di ricerca in less è:\<tar\> .

Questo modello successivo cerca "script di shell", "programma di shell", "variabile di shell", "ambiente di shell" o "prompt di shell" nel nostro documento di prova. Le parentesi alterano l'ordine logico in cui vengono risolti i confronti dei modelli:

[student@studentvm1 testing]$ grep -Eni "\<shell (script|program|variable|environment|prompt)" Experiment_6-3.txt

Nota: Questo articolo è una versione leggermente modificata del capitolo 6 del volume 2 del mio libro Linux, "Using and Administering Linux:Zero to SysAdmin", in uscita da Apress alla fine del 2019.

Rimuovi le parentesi dal comando precedente ed eseguilo di nuovo per vedere la differenza.

Conclusione

Anche se ora abbiamo esplorato gli elementi costitutivi di base delle espressioni regolari in grep , ci sono una varietà infinita di modi in cui possono essere combinati per creare modelli di ricerca complessi ma eleganti. Tuttavia, grep è uno strumento di ricerca e non fornisce alcuna capacità diretta di modificare o modificare una riga di testo nel flusso di dati quando viene effettuata una corrispondenza. A tale scopo, abbiamo bisogno di uno strumento come sed , che tratterò nel mio prossimo articolo.


Linux
  1. Utilizzare Command Grep e individuare?

  2. Taglia / Grep e Df -h?

  3. Grep e coda -f?

  4. Nozioni di base su Vhost e blocchi server

  5. Tutorial Honeypot – Modalità e funzionamento di Honeypot

Grep Regex:una guida completa

Espressioni regolari in Grep (Regex)

KDE Connect sta migliorando sempre di più

Come connettere e condividere dati tra due sistemi Linux

Apache Cassandra:funzionalità e installazione

10 esempi pratici di regex con grep