Come escludere alcuni file che non corrispondono a determinate estensioni con grep?


8

Voglio produrre tutte le righe contenenti la parola in OKmodo ricorsivo da una directory. Ma ci sono alcune estensioni che devo escludere dal risultato:

*~
*.map
*.js except *.debug.js

Provai:

grep -r --exclude={*~,*.map} "OK" /some/dir

Solo che non so come rimuovere dal risultato tutti quei .jsfile non di debug .

Risposte:


7

Vorrei solo passare un secondo grepper rimuoverli:

grep -r --exclude={\*~,\*.map} "OK" bar/ | grep -vP '(?<!debug)\.js'

Il -vrovescio della corrispondenza, la stampa di linee che non corrispondono al modello e -Pabilita le espressioni regolari compatibili Perl che ci consentono di utilizzare lookbehinds negativi . Questa regex particolare corrisponderà a .jsciò che non è preceduto da debugquali mezzi (poiché stiamo invertendo le corrispondenze) che .jsverranno stampati solo quei file.

Tuttavia, come sottolineato da @QuestionOverflow nei commenti, ciò potrebbe avere l'effetto collaterale indesiderato di filtrare le righe che contengono OKe jspoiché grep -vviene applicato all'intero output, non solo il nome del file. Per evitarlo, basta aggiungere due punti (questo è ciò che viene greputilizzato per separare i nomi dei file dal contenuto del file):

grep -r --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js:'

Ciò non riuscirà comunque se la riga di input contiene foo.js:o se il nome del file contiene :. Quindi, per essere sicuri, usa un approccio diverso:

grep -Tr --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js\t'

Le -Tcause grepper stampare una scheda tra il nome del file e il contenuto del file. Quindi, se aggiungiamo semplicemente \ta alla fine del regex, corrisponderà solo ai nomi dei file e non al contenuto della riga.

Tuttavia, l' utilizzofind potrebbe avere più senso a prescindere.


1
Vorrei escludere inavvertitamente le righe nei file desiderati, ma contenenti entrambi OKe .jssulla stessa riga?
Domanda Overflow

@QuestionOverflow ah, sì davvero, buona cattura. Vedi la risposta aggiornata.
terdon

Risposta fantastica. Devo accettare il tuo poiché chiedo specificamente grep. Grazie.
Domanda Overflow

@QuestionOverflow sei il benvenuto. In generale, tuttavia, findè probabilmente meglio per questo tipo di cose. Ottenere il giusto greppuò essere complicato come hai sottolineato :).
terdon

Le tue soluzioni falliscono se si ha l' failglobopzione impostata nella shell: bash: no match: --exclude=*~ è necessario citare gli argomenti del modello GLOB --excludeper nasconderli dall'espansione della shell, ad es.--exclude={\*~,\*.map}
Ian D. Allen

7

Userei findper localizzare i file e inoltrare il risultato attraverso xargs:

$ find . -type f \! -name "*~" \
                 \! -name "*.map" \
                 \! \( -name "*.js" -and \! -name "*.debug.js" \) \
         -print0 | xargs -0 grep "OK"

Questo cerca ogni file che non corrisponde a " *~", " *.map" o " *.jsma non *.debug.js".

Usando findpuoi facilmente cercare regole piuttosto complesse e questo approccio ti salva dalla rimozione accidentale di falsi positivi come potrebbe accadere con il doppio grep.


Bella risposta anche :)
Domanda Overflow

3
Sì, questo è probabilmente il modo migliore, +1. È inoltre possibile utilizzare -exec grep OK {} +anziché xargsed evitare un programma aggiuntivo.
terdon

2
@IDAllen no, nota che ho suggerito di -exec +no -exec \;, che eseguirà il minor numero possibile di comandi, proprio come xargs.
terdon

4

Con zshte puoi fare:

setopt extendedglob
grep OK some/dir/**/^(*~|*.map|(^*debug).js)

A condizione che l'elenco degli argomenti non sia troppo lungo, nel qual caso puoi sempre fare:

printf '%s\0' some/dir/**/^(*~|*.map|(^*debug).js) | xargs -0 grep OK

Inoltre, potresti fare l'ultimo zshsolo: autoload zargsezargs some/dir/**/^(*~|*.map|(^*debug).js) -- grep OK
don_crissti il

2

Se non ti dispiace vedere l'output leggermente fuori ordine (se lo fai, puoi ordinarlo):

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir **/*.debug.js

Ciò richiede che la shell supporti il **globbing ricorsivo: zsh lo fa immediatamente, bash lo fa dopo l'esecuzione shopt -s globstar, ksh93 lo fa dopo l'esecuzione set -o globstar.

Senza **supporto nella shell, puoi usare due comandi grep:

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir
grep -r --include=*.debug.js "OK" /some/dir

La mia shell supporta **, ma sembra che ci sia qualcosa di sbagliato nell'argomento in più **/*.debug.js, facendo interpretare grep OKcome una directory. Hai provato a eseguirlo?
Domanda Overflow

@QuestionOverflow Errore mio, avevo scambiato l'ordine degli argomenti.
Gilles 'SO- smetti di essere malvagio' il

2

Puoi usare ripgrep . Di default ignora i file nascosti e rispetta il tuo .gitignorefile.

È possibile specificare le regole di inclusione o esclusione utilizzando i seguenti parametri:

-g/--glob GLOB Includi o escludi file e directory per la ricerca che corrispondono al glob dato.

-t/--type TYPE Cerca solo i file corrispondenti a TYPE. È possibile fornire più flag di tipo.

-T/ --type-not TYPENon cercare file corrispondenti a TYPE.

Usa il --type-listflag per elencare tutti i tipi disponibili.

Ecco alcuni semplici esempi:

rg -Tjs "OK"                              # Excludes *.js, *.jsx, *.vue files.
rg -tpy "OK"                              # Includes Python files.
rg --type-add 'map:*.map' -tmap PATTERN   # Excludes *.map files.
rg -g '!*.js' -g '*.debug.js' PATTERN     # Excludes *.js apart of *.debug.js.

Ecco la soluzione completa per escludere *.~, *.map, *.js, ma non *.debug.js:

rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' "OK"

test:

$ touch file.~ file.map file.js file.debug.js file.txt file.md
$ rg --files
file.debug.js
file.js
file.map
file.md
file.txt
$ rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' --files
file.debug.js
file.md
file.txt
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.