¿Cómo puedo ejecutar un comando específico para cada resultado de búsqueda?

49

¿Cómo se podría ejecutar un comando específico para cada archivo que se encontró utilizando el findcomando? A los efectos de la pregunta, digamos que simplemente me gustaría eliminar cada archivo encontrado por find.

FailedDev
fuente

Respuestas:

60

Editar: Si bien la siguiente respuesta explica el caso de uso general, debo tener en cuenta que eliminar archivos y directorios es un caso especial. En lugar de usar la -execdir rm {} \;construcción, simplemente use -delete, como en:

find -iname '*.txt' -delete

Esto maneja un montón de casos extremos en los que podría no pensar, incluido el orden de los archivos y directorios que deben eliminarse para no encontrarse con errores. Para otros casos de uso ...

La mejor manera de manejar los comandos en ejecución de los resultados de un hallazgo es usualmente usar las diversas -execopciones para el findcomando. En particular, debe intentar usarlo -execdirsiempre que sea posible, ya que se ejecuta dentro del directorio del archivo que se encontró y generalmente es más seguro (en el sentido de evitar que los errores estúpidos sean desastrosos) que otras opciones.

Las -execopciones son seguidas por el comando que le gustaría ejecutar con {}denotar el lugar donde se debe incluir el archivo encontrado por find y se termina ya sea \;para ejecutar el comando una vez para cada archivo o +para reemplazarlo {}con una lista de argumentos de todas las coincidencias . Tenga en cuenta que el terminador de punto y coma se escapa para que el shell no lo entienda como un separador que conduce a un nuevo comando.

Digamos que estaba encontrando todos los archivos de texto:

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

Aquí está el bit relevante del manual de búsqueda ( 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.
Caleb
fuente
El comando "buscar" proporcionado por Busybox no es compatible con la opción -execdir, por lo tanto, puede ser necesario usar uno de los métodos pipe / xargs mencionados a continuación.
MikeW
9

Una alternativa es canalizar la salida y analizarla con comandos posteriores. La única forma segura de hacerlo es usar la -print0opción, que le indica findque use un carácter nulo como delimitador de resultados. Los comandos de recepción deben tener la capacidad de reconocer entradas delimitadas nulas. Ejemplo:

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

Tenga en cuenta que la -0opción le dice xargsque trate la entrada como nula delimitada.

phunehehe
fuente
Puede -execcon más archivos si lo termina con en +lugar de ;. Ver la respuesta de Caleb.
Kevin
@ Kevin tienes razón, actualicé la respuesta.
phunehehe
3

Find tiene un comando de eliminación incorporado si eso es todo lo que necesita hacer.

find . -name "*.txt" -delete

Cualquier archivo .txt encontrado se eliminará utilizando el comando anterior.

SaultDon
fuente
2

Estaba buscando una respuesta a esto y me topé con este hilo. Las respuestas me dieron una idea de cómo podría lograrlo. Supongamos que desea encontrar el mediainfode todos los archivos JPEG

Esto se agregaría mediainfo "al principio y "al final de cada archivo coincidente (para escapar de caracteres especiales tanto como sea posible), póngalo en un script y ejecute el script:

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

En caso de que le preocupe que algo pueda salir mal, puede omitir la redirección de la salida a un archivo y simplemente ver el resultado en el terminal antes de ejecutar el script.

RuMAN S
fuente
1

Puede lograr esto usando el xargscomando. xargsesencialmente ejecuta un comando una vez para cada instrucción de su entrada estándar. Entonces, si necesita eliminar todos los .jpgarchivos en un directorio, por ejemplo, una forma rápida en la línea de comando es:

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

También puede usar la tecla de retroceso (encima del botón Tabulador) para hacer esto (tenga en cuenta que este es el carácter de comilla inversa, no el carácter de comilla simple):

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

Tenga en cuenta que debido a la forma en que los xargsshells procesan su entrada, el método xargs solo funciona si ninguno de los nombres de archivos y directorios involucrados contiene espacios en blanco o alguno de ellos \"'; el método de comillas inversas solo funciona si ninguno de los nombres de archivos y directorios involucrados contiene espacios en blanco o alguno de ellos \[?*.

IG83
fuente
3
Ambos métodos son potencialmente muy peligrosos, especialmente el backtick. Existen numerosos problemas potenciales con los caracteres no escapados en los nombres de archivo que podrían hacer que estos métodos se rompan.
Caleb
1
Veo su punto, pero estos comandos también se pueden usar junto con otras herramientas además de encontrar, por lo que creo que vale la pena mencionarlos aquí.
IG83
Vale la pena mencionarlos, pero es importante especificar cuándo no usarlos. La pregunta del OP aquí pedía específicamente el manejo de la salida de find, para lo cual -execgeneralmente es una mejor solución. Si desea especificarlos como alternativos, al menos explique cómo usarlos find -print0 | xargs -0para el manejo seguro de la ruptura del nombre de archivo y explique cuándo tener cuidado con los backticks.
Caleb
1
Bienvenido al sitio, por cierto, veo que esta es su primera respuesta. Perdón por saltar por encima. Es importante enseñar a las personas desde el principio los problemas para que no cometan errores que sean difíciles de atrapar más tarde, pero aún recuerdo los días en que no entendía por qué esto también era un problema tan grande, así que por favor no No tome la corrección como algo personal.
Caleb
3
¡Gracias por la bienvenida! Por supuesto, no hay resentimientos, ciertamente tienes razón en que -exec es la forma apropiada de manejar esto. Realmente estoy empezando a ver cuán genial es esta plataforma, estás aprendiendo cosas nuevas incluso de los comentarios :)
IG83