Esegui `grep` escludendo un file in un percorso specifico


12

Voglio escludere il file ./test/main.cppdalla mia ricerca.

Ecco cosa sto vedendo:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

So che è possibile ottenere l'output desiderato usando più comandi in una disposizione di pipe e filtri, ma c'è qualche citazione / escape che farà grepcapire ciò che voglio nativamente?


Una soluzione basata sul filtraggio dell'output non si adatta bene perché cerca inutilmente il file prima di escludere i risultati associati. Il problema è amplificato se voglio escludere intere directory (con --exclude-dir). Ecco perché vorrei che grep eseguisse l'esclusione in modo nativo.
nobar

1
--exclude specifica glob non un percorso
PersianGulf

Risposte:


6

grep non puoi farlo per il file in una determinata directory se hai più file con lo stesso nome in directory diverse, usa invece trova:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


Perché stai scappando \!e \+? Sembra funzionare bene senza le barre rovesciate.
nobar,

@nobar Ci sono abituato perché alcuni personaggi sono parole chiave della shell, quindi non rimarrai mai sorpreso perché nulla può accadere se sono scappati.
MichalH,

" grepnon posso farlo, usa findinvece" - perfetto.
nobar

4

Non penso sia possibile con GNU grep. Non hai bisogno di tubi però.

Con find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

Con zsh:

grep pattern ./**/*~./test/main.cpp(.)

(esclude i file nascosti, anche per escludere .git, .svn ...).


2

Potrei scrivere un libro: "L'arte perduta di xargs". I find ... -exec … ';lanci un grep per ogni file (ma la variante con -exec … +non lo fa). Bene, oggigiorno stiamo sprecando cicli della CPU, quindi perché no, giusto? Ma se le prestazioni, la memoria e la potenza sono un problema: usa xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU find's -print0terminerà il NULsuo output e xargsl' -0opzione onora quel formato come input. Questo assicura che qualunque personaggio divertente abbia il tuo file, la pipeline non verrà confusa. L' -ropzione assicura che non ci siano errori nel caso in cui findnon trovi nulla.

Nota, ora puoi fare cose come:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep's -zfa la stessa cosa di xargs ' -0.


3
Alcune note interessanti, ma non sono sicuro che tu abbia ragione sul problema delle prestazioni. A quanto ho capito, find -exec (cmd) {} +funziona allo stesso modo xargse find -exec (cmd) {} \;funziona allo stesso modo xargs -n1. In altre parole, la tua affermazione è corretta solo se \;viene utilizzata la versione.
nobar

3
Il piping in xargsè meno efficiente rispetto all'uso -exec … +(anche se marginalmente). Nessuna delle risposte qui menziona nemmeno -exec … \;.
Gilles 'SO- smetti di essere malvagio'

1
Bene, s - t. Io esco con me stesso. Grazie per i commenti e le correzioni. Ho pensato che \ + fosse un refuso. Oh guarda, -exec ... +aggiunto nel gennaio 2005. Sì, non sono obsoleto ... a ... tutto.
Otheus,

2

Se i tuoi findsupporti sono -pathstati aggiunti a POSIX nel 2008 ma mancano ancora in Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
Non credo che funzionerà perché Nobar vuole main.cpp in altre directory
Eric Renouf,

1
il tuo pattern non escluderà main.cpp da tutte le altre directory? Non sarebbe desiderabile
Eric Renouf,

@EricRenouf: Oh, errore mio, una lettura errata. Aggiornato la mia risposta.
cuonglm,

@Gilles: Perché -pathPOSIX non è?
cuonglm,

Ah, scusa, errore mio, è stato aggiunto nel 2008. Tuttavia manca ancora da Solaris.
Gilles 'SO- smetti di essere malvagio'

1

Per la cronaca, ecco l'approccio che preferisco:

grep pattern $(find . -type f ! -path './test/main.cpp')

Mantenendo grepall'inizio del comando, penso che questo sia un po 'più chiaro, inoltre non disabilita grepl'evidenziazione del colore. In un certo senso, l'uso findin una sostituzione di comando è solo un modo di estendere / sostituire il sottoinsieme (limitato) di ricerca di file della grepfunzionalità di.


Per me, la find -execsintassi è un po 'arcana. Una complessità find -execè la (a volte) necessità di sfuggire a vari personaggi (in particolare se \;usato sotto Bash). Solo allo scopo di mettere le cose in contesti familiari, i seguenti due comandi sono sostanzialmente equivalenti:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Se si desidera escludere le sottodirectory , potrebbe essere necessario utilizzare un carattere jolly. Non capisco completamente lo schema qui - parla di arcane :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Un'ulteriore nota per generalizzare le findsoluzioni basate su base per l'uso negli script : La grepriga di comando dovrebbe includere l' opzione -H/ --with-filename. Altrimenti cambierà la formattazione dell'output nel caso in cui ci sia un solo nome file nei risultati della ricerca find. Ciò è notevole perché non sembra essere necessario se si utilizza grepla ricerca file nativa (con l' -ropzione).

... Ancora meglio, però, è includere /dev/nullcome primo file da cercare. Questo risolve due problemi:

  • Assicura che se c'è un file da cercare, greppenserà che ce ne sono due e utilizzerà la modalità di output a più file.
  • Assicura che se non ci sono file da cercare, greppenserà che ci sia un file e non si bloccherà in attesa su stdin.

Quindi la risposta finale è:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

Non dovresti usare l'output di findin una sostituzione di comando. Questo si interrompe se ci sono nomi di file contenenti spazi o altri caratteri speciali. Usa find -exec, è robusto e facile da usare.
Gilles 'SO- smetti di essere malvagio'

@Gilles: ottimo punto - anche l'output potrebbe eventualmente superare i limiti di dimensione della riga di comando di alcuni programmi. Caveat emptor.
nobar,

Ugh. La sintassi 'find' è terribilmente difficile. '-o' è un operatore "o" (anche '-o' su Linux), ma il suo uso tipico (ad esempio con '-prune') non si associa concettualmente alla nozione di un logico o. È un funzionale o piuttosto che logico o.
nobar,

Un altro modo per escludere le sottodirectory in base alla corrispondenza un nome: find -iname "*target*" -or -name 'exclude' -prune. Bene, in un certo senso funziona: la directory eliminata verrà elencata, ma non cercata. Se non lo desideri elencato, puoi aggiungere una sorta di ridondante! -name 'exclude'
nobar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.