Ricerca nel sito web

10 esempi pratici di comandi Grep per sviluppatori


Il comando grep viene utilizzato per trovare modelli nei file. Questo tutorial mostra alcuni degli esempi di comandi grep più comuni che potrebbero essere particolarmente utili per gli sviluppatori di software.

Recentemente, ho iniziato a lavorare con Asciidoctor.js e su i modelli Asciidoctor.js-pug e Asciidoctor-template. progetto js.

Non è sempre facile essere immediatamente efficaci quando si scava per la prima volta in un codice contenente diverse migliaia di righe. Ma la mia arma segreta per orientarmi tra così tante righe di codice è lo strumento grep.

Condividerò con te come utilizzare il comando grep in Linux con esempi.

Esempi utili nella vita reale dei comandi grep in Linux

Se guardi nel man, vedrai quella breve descrizione dello strumento grep: “stampa linee che corrispondono a un modello. "

Tuttavia, non lasciarti ingannare da una definizione così umile: grep è uno degli strumenti più utili nella casella degli strumenti Unix e ci sono innumerevoli occasioni per usarlo non appena lavori con file di testo.

È sempre meglio avere esempi del mondo reale per imparare come funzionano le cose. Quindi utilizzerò l'albero dei sorgenti Asciidoctor.js per illustrare alcune delle funzionalità di grep.

Puoi scaricare l'albero dei sorgenti da GitHub e, se vuoi, puoi anche controllare lo stesso changeset che ho usato quando ho scritto questo articolo. Ciò ti garantirà di ottenere risultati perfettamente identici a quelli descritti nel resto di questo articolo:

git clone https://github.com/asciidoctor/asciidoctor.js
cd asciidoctor.js
git checkout v1.5.6-rc.1

1. Trova tutte le occorrenze di una stringa (utilizzo di base)

Asciidoctor.js supporta il motore JavaScript Nashorn per la piattaforma Java. Non conosco Nashorn, quindi potrei cogliere l'occasione per saperne di più esplorando le parti del progetto che fanno riferimento a quel motore JavaScript.

Come punto di partenza, ho controllato se c'erano alcune impostazioni relative a Nashorn nel file package.json che descrivevano le dipendenze del progetto:

linux@handbook:~$ grep nashorn package.json
    "test": "node npm/test/builder.js && node npm/test/unsupported-features.js && node npm/test/jasmine-browser.js && node npm/test/jasmine-browser-min.js && node npm/test/jasmine-node.js && node npm/test/jasmine-webpack.js && npm run test:karmaBrowserify && npm run test:karmaRequirejs && node npm/test/nashorn.js",

Sì, a quanto pare c'erano alcuni test specifici per Nashorn. Quindi, indaghiamo un po’ di più.

2. Ricerca senza distinzione tra maiuscole e minuscole in un set di file

Ora voglio dare un'occhiata più da vicino ai file dalla directory ./npm/test/ che menzionano esplicitamente Nashorn.

Una ricerca senza distinzione tra maiuscole e minuscole (opzione -i) è probabilmente migliore qui poiché ho bisogno di trovare entrambi i riferimenti a nashorn e Nashorn (o qualsiasi altro combinazione di caratteri maiuscoli e minuscoli):

linux@handbook:~$ grep -i nashorn npm/test/*.js
npm/test/nashorn.js:const nashornModule = require('../module/nashorn');
npm/test/nashorn.js:log.task('Nashorn');
npm/test/nashorn.js:nashornModule.nashornRun('jdk1.8.0');

In effetti, la distinzione tra maiuscole e minuscole è stata utile qui. Altrimenti, mi sarei perso l'istruzione require('../module/nashorn'). Senza dubbio dovrei esaminare quel file più dettagliatamente in seguito.

3. Trova tutti i file non corrispondenti

A proposito, ci sono alcuni file non specifici di Nashorm nella directory npm/test/? Per rispondere a questa domanda, possiamo usare l'opzione “stampa file non corrispondenti” di grep (opzione -L):

sh$ grep -iL nashorn npm/test/*
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js

Nota come con l'opzione -L l'output di grep è cambiato per visualizzare solo i nomi dei file. Pertanto, nessuno dei file sopra riportati contiene la stringa “nashorn” (indipendentemente dal caso). Ciò non significa che non siano in qualche modo legati a quella tecnologia, ma almeno le lettere “n-a-s-h-o-r-n” non sono presenti.

4. Trovare modelli in file nascosti e ricorsivamente in sottodirectory

Gli ultimi due comandi utilizzavano un pattern glob di shell per passare l'elenco dei file da esaminare al comando grep.

Tuttavia, ciò presenta alcune limitazioni intrinseche: la stella (*) non corrisponderà ai file nascosti. Né corrisponderà ai file (eventualmente) contenuti nelle sottodirectory.

Una soluzione potrebbe essere quella di combinare grep con il comando find invece di fare affidamento su un modello glob di shell:

# This is not efficient as it will spawn a new grep process for each file
linux@handbook:~$ find npm/test/ -type f -exec grep -iL nashorn \{} \;
# This may have issues with filenames containing space-like characters
linux@handbook:~$ grep -iL nashorn $(find npm/test/ -type f)

Come ho accennato nei commenti nel blocco di codice sopra, ogni soluzione presenta degli inconvenienti.

Per quanto riguarda i nomi di file contenenti caratteri simili a spazi, ti lascio esaminare l'opzione grep -z che, combinata con l'opzione -print0 del comando find , può mitigare questo problema. Non esitare a utilizzare la sezione commenti alla fine di questo articolo per condividere le tue idee su quell'argomento!

Tuttavia, una soluzione migliore utilizzerebbe l’opzione “ricorsiva” di grep. Con questa opzione, fornisci sulla riga di comando la radice del tuo albero di ricerca (la directory di partenza) invece dell'elenco esplicito di nomi di file da esaminare.

Con l'opzione -r, grep cercherà tutti i file nella directory specificata, compresi quelli nascosti, e poi scenderà ricorsivamente in qualsiasi sottodirectory:

linux@handbook:~$ grep -irL nashorn npm/test/npm/
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js

In realtà, con questa opzione, potrei anche iniziare la mia esplorazione un livello sopra per vedere che ci sono test non npm che prendono di mira anche Nashorn:

linux@handbook:~$ grep -irL nashorn npm/

Ti ho lasciato testare quel comando da solo per vederne il risultato; ma come suggerimento, posso dire che dovresti trovare molti più file corrispondenti!

5. Filtrare i file in base al nome (utilizzando le espressioni regolari)

Quindi, sembra che ci siano alcuni test specifici Nashorn in quel progetto. Dato che Nashorn è Java, un'altra domanda che potrebbe essere sollevata sarebbe “ci sono alcuni file sorgente Java nel progetto che menzionano esplicitamente Nashorn? ".

A seconda della versione di grep che usi, ci sono almeno due soluzioni per rispondere a questa domanda.

Il primo è usare grep per trovare tutti i file contenenti il pattern “nashorn”, quindi reindirizzare l'output di quel primo comando a una seconda istanza grep che filtra i non-java file sorgenti:

linux@handbook:~$ grep -ir nashorn ./ | grep "^[^:]*\.java"
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/basic.js"));

La prima metà del comando dovrebbe essere ormai comprensibile. Ma che dire della parte “^[\^:]*\\.java”?

A meno che non specifichi l'opzione -F, grep presuppone che il modello di ricerca sia un'espressione regolare. Ciò significa che, oltre ai caratteri semplici che corrisponderanno alla lettera, hai accesso a una serie di metacaratteri per descrivere modelli più complessi. Il modello che ho usato sopra corrisponderà solo:

  • ^ l'inizio della riga

  • [^:]* seguito da una sequenza di qualsiasi carattere tranne i due punti

  • \. seguito da un punto (il punto ha un significato speciale in regex, quindi ho dovuto proteggerlo con una barra rovesciata per esprimere che volevo una corrispondenza letterale)

  • java e seguito dalle quattro lettere “java. "

In pratica, poiché grep utilizzerà i due punti per separare il nome del file dal contesto, mantengo solo le righe che hanno .java nella sezione del nome del file. Vale la pena menzionarlo corrisponderebbe anche ai nomi di file .javascript. Questo è qualcosa che lascio provare a risolvere da solo, se vuoi.

6. Filtrare i file in base al nome utilizzando grep

Le espressioni regolari sono estremamente potenti. Tuttavia, in quel caso particolare, sembra eccessivo. Senza menzionare la soluzione di cui sopra, dedichiamo tempo a esaminare tutti i file alla ricerca del pattern “nashorn”: la maggior parte dei risultati viene scartata dalla seconda fase della pipeline.

Se stai usando la versione GNU di grep, cosa che è probabile se usi Linux, hai un'altra soluzione con l'opzione --include. Questo indica a grep di cercare solo nei file il cui nome corrisponde al modello glob specificato:

linux@handbook:~$ grep -ir nashorn ./ --include='*.java'
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/basic.js"));

7. Trovare le parole

La cosa interessante del progetto Asciidoctor.js è che è un progetto multilingue. Fondamentalmente, Asciidoctor è scritto in Ruby, quindi, per essere utilizzabile nel mondo JavaScript, deve essere "transpilato" utilizzando Opal, un compilatore source-to-source da Ruby a JavaScript. Un'altra tecnologia che non conoscevo prima.

Quindi, dopo aver esaminato le specificità di Nashorn, mi sono assegnato il compito di comprendere meglio l'API Opal. Come primo passo in questa ricerca, ho cercato tutte le menzioni dell'oggetto globale Opal nei file JavaScript del progetto. Potrebbe apparire nelle affettazioni (Opal =), nell'accesso dei membri (Opal.) o forse anche in altri contesti. Un'espressione regolare sarebbe sufficiente. Tuttavia, ancora una volta, grep ha una soluzione più leggera per risolvere questo caso d'uso comune. Utilizzando l'opzione -w, corrisponderà solo a parole, ovvero modelli preceduti e seguiti da un carattere non verbale. Un carattere non verbale è l'inizio o la fine della riga o qualsiasi carattere che non sia né una lettera, né una cifra, né un carattere di sottolineatura:

linux@handbook:~$ grep -irw --include='*.js' Opal .
...

8. colorare l'output

Non ho copiato l'output del comando precedente poiché ci sono molte corrispondenze. Quando l'output è così denso, potresti voler aggiungere un po' di colore per facilitare la comprensione. Se questo non è già configurato per impostazione predefinita sul tuo sistema, puoi attivare tale funzionalità utilizzando l'opzione GNU --color:

linux@handbook:~$ grep -irw --color=auto --include='*.js' Opal .
...

Dovresti ottenere lo stesso risultato lungo di prima, ma questa volta la stringa di ricerca dovrebbe apparire a colori se non lo era già.

9. Conteggio delle righe corrispondenti o dei file corrispondenti

Ho menzionato due volte che l'output dei comandi precedenti era molto lungo. Quanto tempo esattamente?

linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86

Ciò significa che abbiamo un totale 86 righe corrispondenti in tutti i file esaminati. Tuttavia, quanti file diversi corrispondono? Con l'opzione -l puoi limitare l'output di grep ai file corrispondenti invece di visualizzare i < corrispondentirighe. Quindi quella semplice modifica indicherà quanti file corrispondono:

linux@handbook:~$ grep -irwl --include='*.js' Opal . | wc -l
20

Se questo ti ricorda l'opzione -L, nessuna sorpresa: poiché è relativamente comune, le lettere minuscole/maiuscole vengono utilizzate per distinguere le opzioni complementari. -l mostra i nomi di file corrispondenti. -L visualizza nomi di file non corrispondenti. Per un altro esempio, ti lascio controllare il manuale per le opzioni -h/-H.

Chiudiamo la parentesi e torniamo al nostro risultato: 86 righe corrispondenti. 20 file corrispondenti. Tuttavia, come vengono distribuite le righe corrispondenti nei file corrispondenti? Possiamo sapere che utilizzando l'opzione -c di grep conterà il numero di righe corrispondenti per file esaminato (compresi i file con zero corrispondenze):

linux@handbook:~$ grep -irwc --include='*.js' Opal .
...

Spesso, quell'output necessita di una post-elaborazione poiché mostra i risultati nell'ordine in cui i file sono stati esaminati e include anche file senza alcuna corrispondenza, cosa che di solito non ci interessa. Quest'ultimo è abbastanza facile da risolvere:

linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$'

Per quanto riguarda l'ordinamento delle cose, puoi aggiungere il comando sort alla fine della pipeline:

linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$' | sort -t: -k2n

Ti lascio controllare il manuale del comando sort per il significato esatto delle opzioni che ho usato. Non dimenticare di condividere i tuoi risultati utilizzando la sezione commenti qui sotto!

10. Trovare la differenza tra due insiemi corrispondenti

Se ricordi, qualche comando fa, ho cercato la parola “Opal. " Tuttavia, se cerco nello stesso set di file tutte le occorrenze della stringa "Opal", ottengo una ventina di risposte in più:

linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86
linux@handbook:~$ grep -ir --include='*.js' Opal . | wc -l
105

Trovare la differenza tra questi due insiemi sarebbe interessante. Allora, quali sono le righe contenenti le quattro lettere “opale” di fila, ma dove quelle quattro lettere non formano un'intera parola?

Non è così facile rispondere a questa domanda. Perché la stessa riga può contenere sia la parola Opale sia una parola più grande contenente quelle quattro lettere. Ma come prima approssimazione, potresti usare quella pipeline:

linux@handbook:~$ grep -ir --include='*.js' Opal . | grep -ivw Opal
./npm/examples.js:  const opalBuilder = OpalBuilder.create();
./npm/examples.js:  opalBuilder.appendPaths('build/asciidoctor/lib');
./npm/examples.js:  opalBuilder.appendPaths('lib');
...

A quanto pare, la mia prossima tappa sarà quella di indagare sull'oggetto opalBuilder, ma sarà per un altro giorno.

L'ultima parola

Naturalmente, non capirai l'organizzazione di un progetto, e ancor meno l'architettura del codice, semplicemente eseguendo un paio di comandi grep!

Tuttavia, trovo che il comando sia inevitabile per identificare benchmark e punti di partenza quando si esplora una nuova base di codice.

Quindi, spero che questo articolo ti abbia aiutato a comprendere la potenza del comando grep e che lo aggiungerai alla tua cassetta degli attrezzi. Senza dubbio non te ne pentirai!

Articoli correlati: