Usa il comando find ma escludi i file in due directory


86

Voglio trovare i file che terminano con _peaks.bed, ma escludo i file nelle cartelle tmpe scripts.

Il mio comando è così:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Ma non ha funzionato. I file in tmpe nella scriptcartella verranno comunque visualizzati.

Qualcuno ha idee su questo?

Risposte:


189

Ecco come puoi specificarlo con find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Spiegazione:

  • find . - Avvia la ricerca dalla directory di lavoro corrente (in modo ricorsivo per impostazione predefinita)
  • -type f- Specifica findche vuoi solo i file nei risultati
  • -name "*_peaks.bed" - Cerca i file con il nome che termina con _peaks.bed
  • ! -path "./tmp/*" - Escludi tutti i risultati il ​​cui percorso inizia con ./tmp/
  • ! -path "./scripts/*" - Escludi anche tutti i risultati il ​​cui percorso inizia con ./scripts/

Testare la soluzione:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Eri abbastanza vicino, l' -nameopzione considera solo il nome di base, dove -pathconsidera l'intero percorso =)


Bel lavoro. Tuttavia, hai dimenticato una delle cose che l'OP voleva, trovare i file che terminano con _peaks.bed.
alex

2
Questo utilizza una serie di estensioni in GNU find, ma poiché la domanda è etichettata Linux, questo non è un problema. Buona risposta.
Jonathan Leffler

1
Una breve nota: se usi .al prompt di ricerca iniziale, devi usarlo in ogni percorso che escludi. La corrispondenza del percorso è piuttosto rigorosa, non esegue ricerche sfocate. Quindi se lo usi find / -type f -name *.bed" ! -path "./tmp/"non funzionerà. devi ! -path "/tmp"renderlo felice.
Peelman

3
È importante notare che * è importante. $ ! -path "./directory/*"
Thomas Bennett

3
Secondo le pagine man: "Per ignorare un intero albero di directory, usa -pruneinvece di controllare ogni file nell'albero." Se le tue directory escluse sono molto profonde o contengono tonnellate di file e ti interessano le prestazioni, usa -pruneinvece l' opzione.
thdoan

8

Ecco un modo per farlo ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"

2
Questo ha il merito di lavorare con qualsiasi versione di find, piuttosto che solo con GNU find. Tuttavia, la domanda è etichettata Linux, quindi non è critica.
Jonathan Leffler

2

Uso

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

o

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

o

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

L'ordine è importante. Valuta da sinistra a destra. Inizia sempre con l'esclusione del percorso.

Spiegazione

Non utilizzare -not(o !) per escludere l'intera directory. Usa -prune. Come spiegato nel manuale:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

e nel manuale GNU trova:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

Infatti, se usi -not -path "./pathname", find valuterà l'espressione per ogni nodo sotto "./pathname".

le espressioni find sono solo una valutazione delle condizioni.

  • \( \)- operazione di gruppo (puoi usare -path "./tmp" -prune -o -path "./scripts" -prune -o, ma è più prolissa).
  • -path "./script" -prune- se -pathrestituisce true ed è una directory, restituisce true per quella directory e non scendere in essa.
  • -path "./script" ! -prune- valuta come (-path "./script") AND (! -prune). Ripristina il "sempre vero" di potare in sempre falso. Evita la stampa "./script"come corrispondenza.
  • -path "./script" -prune -false- poiché -pruneritorna sempre vero, puoi seguirlo con -falseper fare lo stesso di !.
  • -o- Operatore OR. Se non viene specificato alcun operatore tra due espressioni, il valore predefinito è l'operatore AND.

Quindi, \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printè espanso a:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

La stampa è importante qui perché senza di essa è espansa a:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printviene aggiunto da find - ecco perché la maggior parte delle volte non è necessario aggiungerlo nell'espressione. E poiché -prunerestituisce true, stamperà "./script" e "./tmp".

Non è necessario negli altri perché siamo passati -prunea restituire sempre false.

Suggerimento: puoi usare find -D opt expr 2>&1 1>/dev/nullper vedere come è ottimizzato ed espanso,
find -D search expr 2>&1 1>/dev/nullper vedere quale percorso è controllato.


0

Prova qualcosa di simile

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

e non essere troppo sorpreso se ho sbagliato un po '. Se l'obiettivo è un exec (invece di print), sostituiscilo semplicemente al suo posto.


0

per me, questa soluzione non ha funzionato su un comando exec con find, non so davvero perché, quindi la mia soluzione è

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Spiegazione: uguale a quello di sampson-chen con l'aggiunta di

-prune - ignora il percorso procedente di ...

-o - Quindi se nessuna corrispondenza stampa i risultati, (elimina le directory e stampa i risultati rimanenti)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz

La risposta accettata non ha funzionato, ma funziona. Utilizzando prugna, find . -path ./scripts -prune -name '*_peaks.bed' -type f. Non sono sicuro di come escludere più directory. Questo elenca anche la directory esclusa di livello superiore anche se typeè specificato. L'esclusione tramite Grep sembra più semplice a meno che non si desideri utilizzare prune per accelerare l'operazione di ricerca.
Mohnish,

Ho avuto anche problemi ad escludere più directory, ma i commenti sopra mi hanno dato una risposta che ha funzionato. Uso più istanze di '-not -path' e in ogni espressione di percorso includo il prefisso completo come usato nel primo parametro per 'trovare' e terminare ciascuna con un asterisco (ed evitare qualsiasi punto).
jet il

0

Puoi provare di seguito:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'

2
Su una vecchia domanda come quella (4 anni!) Vuoi spiegare perché questa nuova risposta è migliore o diversa, non solo codice "dump".
Nic3500
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.