Come posso eseguire un comando specifico per ogni risultato di ricerca?


49

Come eseguire un comando specifico per ciascun file trovato utilizzando il findcomando? Ai fini della domanda diciamo che vorrei semplicemente eliminare ogni file trovato da find.

Risposte:


60

Modifica: Mentre la seguente risposta spiega il caso d'uso generale, dovrei notare che la cancellazione di file e directory è un caso speciale. Invece di usare il -execdir rm {} \;costrutto, basta usare -delete, come in:

find -iname '*.txt' -delete

Questo gestisce una serie di casi limite ai quali potresti non pensare di includere quali file e directory di ordine devono essere eliminati per non incorrere in errori. Per altri casi d'uso ...

Il modo migliore per gestire l'esecuzione dei comandi dei risultati di una ricerca è di solito utilizzare le varie -execopzioni per il findcomando. In particolare, dovresti provare a usarlo -execdirogni volta che è possibile poiché viene eseguito all'interno della directory del file che è stato trovato ed è generalmente più sicuro (nel senso di prevenire disastrosi errori stupidi) rispetto ad altre opzioni.

Le -execopzioni sono seguite dal comando che si desidera eseguire {}indicando il punto in cui deve essere incluso il file trovato da find e sono terminate \;eseguendo il comando una volta per ciascun file o +sostituendolo {}con un elenco di argomenti di tutte le corrispondenze . Si noti che il punto e virgola viene escluso in modo che non sia compreso dalla shell come un separatore che porta a un nuovo comando.

Diciamo che stavi trovando tutti i file di testo:

find -iname '*.txt' -execdir rm {} \;

Ecco il bit rilevante dal manuale find ( man find):

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of ‘;’ is encountered.  The string ‘{}’
          is replaced by the current file name being processed  everywhere
          it occurs in the arguments to the command, not just in arguments
          where it is alone, as in some versions of find.  Both  of  these
          constructions might need to be escaped (with a ‘\’) or quoted to
          protect them from expansion by the shell.  See the EXAMPLES sec-
          tion for examples of the use of the -exec option.  The specified
          command is run once for each matched file.  The command is  exe-
          cuted  in  the starting directory.   There are unavoidable secu-
          rity problems surrounding use of the -exec  action;  you  should
          use the -execdir option instead.


   -exec command {} +
          This  variant  of the -exec action runs the specified command on
          the selected files, but the command line is built  by  appending
          each  selected file name at the end; the total number of invoca-
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  ‘{}’
          is  allowed  within the command.  The command is executed in the
          starting directory.


   -execdir command ;

   -execdir command {} +
          Like -exec, but the specified command is run from the  subdirec-
          tory  containing  the  matched  file,  which is not normally the
          directory in which you started find.  This a  much  more  secure
          method  for invoking commands, as it avoids race conditions dur-
          ing resolution of the paths to the matched files.  As  with  the
          -exec action, the ‘+’ form of -execdir will build a command line
          to process more than one matched file, but any given  invocation
          of command will only list files that exist in the same subdirec-
          tory.  If you use this option, you must ensure that  your  $PATH
          environment  variable  does  not  reference  ‘.’;  otherwise, an
          attacker can run any commands they like by leaving an  appropri-
          ately-named  file in a directory in which you will run -execdir.
          The same applies to having entries in $PATH which are  empty  or
          which are not absolute directory names.

Il comando "trova" fornito da Busybox non supporta l'opzione -execdir, quindi potrebbe essere necessario utilizzare uno dei metodi pipe / xargs menzionati di seguito.
MikeW,

9

Un'alternativa è convogliare l'output e analizzarlo con i comandi successivi. L'unico modo sicuro per farlo è usare l' -print0opzione, che dice finddi usare un carattere null come delimitatore dei risultati. I comandi di ricezione devono essere in grado di riconoscere input delimitati da null. Esempio:

find /home/phunehehe -iregex '.*\.png$' -print0 | xargs -0 file

Si noti che l' -0opzione dice xargsdi trattare l'input come delimitato da null.


Puoi -execcon più file se lo finisci con +invece di ;. Vedi la risposta di caleb.
Kevin,

@Kevin hai ragione, ho aggiornato la risposta.
phunehehe,

3

Trova ha un comando di eliminazione incorporato se questo è tutto ciò che devi fare.

find . -name "*.txt" -delete

Qualsiasi file .txt trovato verrà eliminato utilizzando il comando sopra.


2

Stavo cercando una risposta a questo e mi sono imbattuto in questo thread. Le risposte mi hanno dato e un'idea su come potrei raggiungerlo. Supponiamo di voler trovare mediainfotutti i file JPEG

Questo aggiungerebbe mediainfo "all'inizio e "alla fine di ogni file corrispondente (per sfuggire il più possibile ai caratteri speciali), inserirlo in uno script ed eseguirlo:

find . -name *.jpg | sed -e 's/^/mediainfo "/g;' | sed -e 's/$/"/g;' > foo.sh && sh foo.sh

Se sei preoccupato che qualcosa possa andare storto, puoi saltare il reindirizzamento dell'output su un file e vedere il risultato nel terminale prima di eseguire lo script.


1

Puoi farlo usando il xargscomando. xargsessenzialmente esegue un comando una volta per ogni istruzione del suo input standard. Pertanto, se è necessario eliminare tutti i .jpgfile in una directory, ad esempio, un modo rapido sulla riga di comando è:

$ find ./ -name "*.jpg" | xargs rm 

Puoi anche usare il backtick (sopra il pulsante Tab) per farlo (nota che questo è il carattere di backquote, non il singolo carattere di citazione):

$ rm `find ./ -name "*.jpg"`

Si noti che a causa del modo in cui le xargsshell elaborano il loro input, il metodo xargs funziona solo se nessuno dei nomi dei file e delle directory in questione contiene spazi bianchi o uno qualsiasi \"'; il metodo backquote funziona solo se nessuno dei nomi di file e di directory coinvolti contiene spazi bianchi o uno qualsiasi \[?*.


3
Entrambi questi metodi sono potenzialmente molto pericolosi, specialmente quello di backtick. Esistono numerosi potenziali problemi con i caratteri senza escape nei nomi dei file che potrebbero causare l'interruzione di questi metodi.
Caleb,

1
Vedo il tuo punto, ma questi comandi possono anche essere usati insieme ad altri strumenti oltre a find, quindi penso che valga la pena menzionarli qui.
IG83,

Potrebbero valere la pena menzionarli, ma quando non usarli è importante specificare. La domanda del PO qui ha specificamente chiesto di gestire l'output di find, per il quale -execè generalmente una soluzione migliore. Se si desidera specificarli come sostituti, almeno spiegare come utilizzare find -print0 | xargs -0per la gestione sicura dell'interruzione del nome file ed elaborare quando prestare attenzione ai backtick.
Caleb,

1
Benvenuto sul sito, vedo che questa è la tua prima risposta. Mi dispiace saltarci sopra. È importante insegnare alle persone in anticipo che i problemi sono così che non commettano errori difficili da rilevare in seguito, ma ricordo ancora i giorni in cui non capivo perché anche questo fosse un problema così grande, quindi per favore don prendi la correzione come personale.
Caleb,

3
Grazie per il benvenuto! Ovviamente non ci sono sentimenti duri, hai certamente ragione nel fatto che -exec è il modo appropriato di gestirlo. Sto davvero cominciando a vedere quanto è grande questa piattaforma, stai imparando nuove cose anche dai commenti :)
IG83
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.