Come usare l'opzione '-prune' di 'find' in sh?


219

Non capisco bene l'esempio dato da man findqualcuno, qualcuno può darmi alcuni esempi e spiegazioni? Posso combinare un'espressione regolare in esso?


La domanda più dettagliata è questa:

Scrivi uno script di shell changeall, che ha un'interfaccia simile changeall [-r|-R] "string1" "string2". Si troverà tutti i file con un suffisso di .h, .C, .cc, o .cppe cambiare tutte le occorrenze di string1a string2. -rè un'opzione per rimanere solo nella directory corrente o includere i sottodir.

NOTA:

  1. Per i casi non ricorsivi, lsNON è consentito, potremmo utilizzare solo finde sed.
  2. Ho provato find -depthma NON è stato supportato. Ecco perché mi chiedevo se -prunepotesse aiutare, ma non ho capito l'esempio man find.

EDIT2: stavo facendo un incarico, non ho fatto domande in dettaglio perché mi piacerebbe finirlo da solo. Dal momento che l'ho già fatto e consegnato, ora posso esprimere l'intera domanda. Inoltre, sono riuscito a completare il compito senza utilizzarlo -prune, ma vorrei impararlo comunque.

Risposte:


438

La cosa di cui ho trovato confuso -pruneè che si tratta di un'azione (come -print), non di un test (come -name). Modifica l'elenco delle cose da fare, ma restituisce sempre true .

Lo schema generale per l'utilizzo -pruneè questo:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

Praticamente vuoi sempre -o(OR logico) immediatamente dopo -prune, perché quella prima parte del test (fino a e incluso -prune) restituirà false per le cose che vuoi effettivamente (cioè: quelle che non vuoi eliminare).

Ecco un esempio:

find . -name .snapshot -prune -o -name '*.foo' -print

Questo troverà i file "* .foo" che non sono nelle directory ".snapshot". In questo esempio, -name .snapshotcompone il [conditions to prune], ed -name '*.foo' -printè [your usual conditions]e [actions to perform].

Note importanti :

  1. Se tutto ciò che vuoi fare è stampare i risultati, potresti essere abituato a tralasciare l' -printazione. In genere non si desidera farlo quando si utilizza -prune.

    Il comportamento predefinito di find è "e" l' intera espressione con l' -printazione se non ci sono azioni diverse da -prune(ironicamente) alla fine. Ciò significa che scrivendo questo:

    find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    equivale a scrivere questo:

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

    il che significa che stamperà anche il nome della directory che stai potando, che di solito non è quello che vuoi. Invece è meglio specificare esplicitamente l' -printazione se è quello che vuoi:

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. Se la tua "solita condizione" corrisponde a file che corrispondono anche alla tua condizione di prugna, tali file non saranno inclusi nell'output. Il modo per risolvere questo problema è aggiungere un -type dpredicato alla condizione della prugna.

    Ad esempio, supponiamo di voler eliminare qualsiasi directory che ha avuto inizio .git(questo è un po 'inventato - normalmente devi solo rimuovere la cosa chiamata esattamente .git ), ma a parte quello volevo vedere tutti i file, inclusi file come .gitignore. Potresti provare questo:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    Ciò non includerebbe .gitignorenell'output. Ecco la versione fissa:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

Suggerimento extra: se stai usando la versione GNU di find, la pagina texinfo per findha una spiegazione più dettagliata della sua manpage (come è vero per la maggior parte delle utility GNU).


6
non è ovvio al 100% nel tuo testo (ma poiché stampi solo '* .foo' non è in conflitto) ma anche la parte -prune non stamperà nulla (non solo directory) chiamato ".snapshot". cioè, -prunenon funziona solo con le directory (ma, per le directory, impedisce anche di inserire le directory corrispondenti a quella condizione, ovvero qui le directory corrispondenti a quella -name .snapshot).
Olivier Dulac il

12
e +1 per te per la spiegazione ben fatta (e soprattutto la nota importante). Dovresti inviarlo ai cercatori di sviluppo (poiché la pagina man non spiega "potare" per gli esseri umani normali ^^ Mi ci sono voluti molti tentativi per capirlo, e non ho visto l'effetto collaterale di cui ci avverti)
Olivier Dulac il

2
@OlivierDulac Questo è un ottimo punto per eliminare potenzialmente i file che vuoi conservare. Ho aggiornato la risposta per chiarire questo. a dire il vero non è la -prunestessa causa. Il problema è che l'operatore o "cortocircuita" e ha una precedenza inferiore rispetto a e. Il risultato finale è che se .snapshotsi incontra un file chiamato, esso corrisponderà al primo -name, -prunequindi non farà nulla (ma restituirà true), e quindi o restituirà true poiché il suo argomento di sinistra era vero. L'azione (ad esempio:) -printfa parte del suo secondo argomento, quindi non ha mai la possibilità di essere eseguita.
Laurence Gonsalves il

3
+1 finalmente ho scoperto perché ho bisogno -printalla fine, ora posso smettere di aggiungere \! -path <pattern>oltre a-prune
Miserable Variable

6
Si noti che "-o" è una scorciatoia per "-o", che (sebbene non conforme a POSIX) legge più chiaramente.
yoyo

27

Normalmente il modo nativo di fare le cose in Linux e il modo in cui pensiamo è da sinistra a destra.
Quindi andresti a scrivere prima quello che stai cercando:

find / -name "*.php"

Quindi probabilmente premi invio e ti rendi conto che stai ricevendo troppi file dalle directory che non desideri. Escludiamo / media per evitare di cercare le unità montate.
Ora dovresti semplicemente APPENDERE quanto segue al comando precedente:

-print -o -path '/media' -prune

quindi il comando finale è:

find / -name "*.php" -print -o -path '/media' -prune

............... | <--- Includi ---> | .................... | <- -------- Escludi ---------> |

Penso che questa struttura sia molto più semplice e sia correlata al giusto approccio


3
Non mi sarei aspettato che questo fosse efficace - avrei pensato che avrebbe valutato la clausola di sinistra prima della prugna, ma con mia sorpresa un rapido test sembra suggerire che findè abbastanza intelligente da elaborare -pruneprima la clausola. Hmmm, interessante.
artfulrobot

Non l'ho mai considerato che in quasi un decennio di utilizzo di GNU trovi! Grazie per questo! Certamente cambierà il modo in cui penso -pruneda ora in poi.
Felipe Alvarez,

3
@artfulrobot Lo sta davvero elaborando per primo? Avrei pensato che stesse entrando /media, notando che non era chiamato *.phpe poi verificando se è attualmente dentro /media, visto che lo è e quindi saltando l'intera sottostruttura. È ancora da sinistra a destra, non fa alcuna differenza purché entrambi i controlli non si sovrappongano.
phk,

26

Attenzione che -prune non impedisce di scendere in nessuna directory come alcuni hanno detto. Impedisce la discesa in directory corrispondenti al test a cui è applicato. Forse alcuni esempi aiuteranno (vedi il fondo per un esempio regex). Mi dispiace per questo essere così lungo.

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

se anche "tocchi ./dir1/scripts/test" (ovvero, hai un file "test", e non dir, in quel sottodir stampato), non verrà stampato da find . -name test -prune -o -print: iow, -pruneè un'azione che funziona su file
Olivier Dulac il

10

Aggiunta al consiglio dato in altre risposte (non ho un rappresentante per creare risposte) ...

Quando si combina -prunecon altre espressioni, esiste una sottile differenza nel comportamento a seconda delle altre espressioni utilizzate.

L'esempio di @Laurence Gonsalves troverà i file "* .foo" che non si trovano nelle directory ".snapshot": -

find . -name .snapshot -prune -o -name '*.foo' -print

Tuttavia, questa .snapshotscorciatoia leggermente diversa elencherà, forse inavvertitamente, anche la directory (e qualsiasi directory .snapshot nidificata): -

find . -name .snapshot -prune -o -name '*.foo'

Il motivo è (secondo la manpage sul mio sistema): -

Se l'espressione data non contiene nessuna delle primarie -exec, -ls, -ok o -print, l'espressione data viene effettivamente sostituita da:

(given_expression) -print

Cioè, il secondo esempio è l'equivalente di inserire il seguente, modificando in tal modo il raggruppamento di termini: -

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

Questo è stato visto almeno su Solaris 5.10. Avendo usato vari tipi di * nix per circa 10 anni, ho cercato solo di recente un motivo per cui ciò si verifica.


Grazie per aver richiamato l'attenzione sulla differenza tra l'utilizzo -prunecon e senza -print!
Mc

3

Prune è un non recurse in qualsiasi switch di directory.

Dalla pagina man

Se -depth non è dato, vero; se il file è una directory, non scendere in esso. Se viene specificato -depth, false; nessun effetto.

Fondamentalmente non desidererà alcuna sottodirectory.

Prendi questo esempio:

Hai le seguenti directory

  • / Home / test2
  • / Home / test2 / test2

Se corri find -name test2:

Restituirà entrambe le directory

Se corri find -name test2 -prune:

Restituirà solo / home / test2 in quanto non scenderà in / home / test2 per trovare / home / test2 / test2


non vero al 100%: si tratta di "eseguire la potatura quando si soddisfano le condizioni e, se si tratta di una directory, eliminarla dall'elenco delle cose da fare, ovvero non inserirla anch'essa". -prune funziona anche sui file.
Olivier Dulac il

2

Non sono esperto in questo (e questa pagina è stata molto utile insieme a http://mywiki.wooledge.org/UsingFind )

Ho appena notato che -pathè un percorso che corrisponde completamente alla stringa / percorso che viene subito dopofind ( .in questi esempi) dove -namecorrisponde a tutti i nomi di base.

find . -path ./.git  -prune -o -name file  -print

blocca la directory .git nella directory corrente ( come si trova in . )

find . -name .git  -prune -o -name file  -print

blocca ricorsivamente tutte le sottodirectory .git.

Nota che ./ è estremamente importante !! -pathdeve corrispondere a un percorso ancorato . o qualunque cosa venga subito dopo la ricerca se si ottengono corrispondenze senza (dall'altra parte del o ' -o') probabilmente non vengono potate! Ero ingenuamente inconsapevole di ciò e mi ha messo a usare -path quando è fantastico quando non vuoi potare tutte le sottodirectory con lo stesso nome di base: D


Nota che se find bla/stai dicendo allora avresti bisogno di -path bla/.git (o se avessi spinto a *in avanti invece si sarebbe comportato più come -name)
sabgenton

1

Mostra tutto incluso dir stesso ma non i suoi lunghi contenuti noiosi:

find . -print -name dir -prune

0

Se leggi tutte le buone risposte qui, la mia comprensione ora è che tutte le seguenti restituiscono gli stessi risultati:

find . -path ./dir1\*  -prune -o -print

find . -path ./dir1  -prune -o -print

find . -path ./dir1\*  -o -print
#look no prune at all!

Ma l'ultimo richiederà molto più tempo poiché cerca ancora tutto in dir1. Immagino che la vera domanda sia come -orottenere risultati indesiderati senza cercarli.

Quindi immagino che la prugna non significhi partite passate decenti, ma contrassegnale come fatto ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "Ciò non è tuttavia dovuto all'effetto dell'azione" -prune "(che impedisce solo un'ulteriore discesa, non assicura ignoriamo quell'elemento). Invece, questo effetto è dovuto all'uso di '-o'. Dato che il lato sinistro della condizione “o” è riuscito per ./src/emacs, non è necessario valutare il diritto- lato ("-print") per questo particolare file. "


0

findcrea un elenco di file. Applica il predicato che hai fornito a ciascuno e restituisce quelli che passano.

Questa idea che -prunesignifica escludere dai risultati mi ha davvero confuso. Puoi escludere un file senza potare:

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

Tutto ciò che -prunefa è alterare il comportamento della ricerca. Se la corrispondenza corrente è una directory, dice "hey find, quel file che hai appena abbinato, non scendere in esso" . Rimuove semplicemente quell'albero (ma non il file stesso) dall'elenco dei file da cercare.

Dovrebbe essere chiamato -dont-descend.


0

Ci sono alcune risposte; alcuni sono un po 'troppo teorici. Lascerò perché una volta avevo bisogno di potare , quindi forse il bisogno del primo / esempio di spiegazione è utile a qualcuno :)

Problema

Avevo una cartella con circa 20 directory di nodo, ognuna con la sua node_modulesdirectory come previsto.

Una volta che entri in qualsiasi progetto, vedi ciascuno ../node_modules/module. Ma sai com'è. Quasi ogni modulo ha dipendenze, quindi quello che stai guardando è più simileprojectN/node_modules/moduleX/node_modules/moduleZ...

Non volevo annegare con un elenco con la dipendenza della dipendenza di ...

Sapere -d n/ -depth n, non mi avrebbe aiutato, poiché la directory principale / first node_modules che volevo di ogni progetto era a una profondità diversa, in questo modo:

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

Come posso ottenere il primo un elenco di percorsi che terminano nel primo node_modulese passare al progetto successivo per ottenere lo stesso?

accedere -prune

Quando aggiungi -prune, avrai comunque una ricerca ricorsiva standard. Ogni "percorso" viene analizzato e ogni scoperta viene espulsa e findcontinua a scavare come un bravo ragazzo. Ma è scavare di più per node_modulesquello che non volevo.

Quindi, la differenza è che in una di queste differenti percorsi, -prunesarà finda smettere di scavare più in basso quella particolare strada quando ha trovato il vostro articolo. Nel mio caso, la node_modulescartella.

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.