¿Cómo integrar el comando mv después del comando find?

61

Estoy buscando archivos cuyo nombre contenga AAAdentro de su ruta usando el siguiente comando:

find path_A -name "*AAA*"

Dado el resultado mostrado por el comando anterior, quiero mover esos archivos a otra ruta, por ejemplo path_B. En lugar de mover esos archivos uno por uno, ¿puedo optimizar el comando moviendo esos archivos justo después del comando find?

huahsin68
fuente

Respuestas:

87

Con GNU mv :

find path_A -name '*AAA*' -exec mv -t path_B {} +

Eso utilizará la -execopción de búsqueda que reemplaza {}a cada resultado de búsqueda a su vez y ejecuta el comando que le das. Como se explica en 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.  

En este caso, estamos utilizando la +versión de -execpara ejecutar la menor cantidad de mvoperaciones posible:

   -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.
Cuonglm
fuente
No olvides el "\;" al final del ejecutivo!
Charles Roth
@Charles Roth: es lo +que hace el trabajo, puedes leer mi cita anterior o en su man findlugar
cuonglm
¡Gracias por esto! Estaba tratando de usar -exec mv {} path_b +y estaba fallando con los errores de permisos. TBH, todavía no entiendo por qué, ¡pero -exec mv -t path_b {} +funciona de maravilla!
Jeremy Davis
66
@JeremyDavis Cuando se usa -exec ... {} +, el {}tiene que ser lo último antes del +. Es por eso que usa mv -t destdir {} +y no mv {} destdir +. En -exec mv {} destdir ';'cambio, se utilizó un resfriado , pero eso se habría ejecutado mvuna vez para cada archivo.
Kusalananda
23

También podrías hacer algo como a continuación.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Dónde,

  1. -0Si hay espacios o caracteres en blanco (incluidas las nuevas líneas), muchos comandos no funcionarán. Esta opción se ocupa de los nombres de archivos con espacio en blanco.
  2. -IReemplace las ocurrencias de replace-str en los argumentos iniciales con nombres leídos de la entrada estándar. Además, los espacios en blanco sin comillas no terminan los elementos de entrada; en cambio, el separador es el carácter de nueva línea.

Pruebas

Creé dos directorios como sourcediry destdir. Ahora, creé un montón de archivos dentro sourcedircomo file1.bak, file2.bakyfile3 with spaces.bak

Ahora, ejecuté el comando como,

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Ahora, dentro de destdir, cuando lo hago ls, puedo ver que los archivos se han movido de sourcedira destdir.

Referencias

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Ramesh
fuente
22

Para beneficio de los usuarios de OS X que se encuentran con esta pregunta, la sintaxis en OS X es ligeramente diferente. Suponiendo que no desea buscar de forma recursiva en subdirectorios de path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Si desea buscar todos los archivos de forma recursiva en path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;
mannykary
fuente
Esta no es una sintaxis específica de OS X, es lo mismo con cualquiera findque haya usado. Puntos -maxdepthpositivos : (especialmente si path_B es un subdirectorio, ¡evita mvintentar mover archivos que ya están allí!) Y usar \; (por lo que {} no tiene que ser el último parámetro y mvse puede usar la sintaxis normal )
drevicko
6

La -execes la mejor manera de hacer esto. Si, por alguna razón, esta no es una opción, también puede leer los resultados en un bucle:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

Esa es la forma segura, puede tratar con nombres de archivos que contienen espacios, líneas nuevas u otros caracteres extraños. Una forma más simple, pero que falla a menos que los nombres de sus archivos consistan solo en caracteres alfanuméricos simples , es

mv $(find path_A -name "*AAA*") path_B

Pero usa el bucle while.

terdon
fuente
La forma más simple aún puede fallar si your file names consist only of simple alphanumeric characters, por ejemplo, si llegaARG_MAX