Ocasionalmente podemos necesitar mover o copiar un conjunto de ficheros ubicados en distintos directorios a uno solo. O incluso simplemente borrarlos. Con una sencilla linea podemos realizar una operación de búsqueda con el comando find y posteriormente procesar cada linea para realizar la operación que nos interesa. Este es uno de los ejercicios clásicos que demuestran la potencia y sencillez de la linea de comandos.

ACTUALIZACIÓN 29/10/2013: Gracias a Ángel Martín González por su apunte sobre el método find (…) -exec, para realizar este tipo de operaciones.

El comando find

Por ejemplo, supongamos que nos interesa mover los ficheros *.avi en diferentes ubicaciones de nuestro sistema de archivos a un sólo directorio. Obiavemente utilizaremos el comando find para localizar los ficheros. Supongamos que queremos buscar en todos los directorios dependientes de nuestra ubicación actual.

find . -type f -iname *.avi

A continuación deberemos de procesar cada linea para realizar la operación. Contamos con múltiples alternativas, pudiendo utilizar diferentes parsers para realizar la operación. Utilizaremos awk y xargs para estos sencillos ejemplos.

Uso de awk

Tal y como recoge la wikipedia AWK es un lenguaje de programación que utiliza listas y expresiones regulares para el procesado de cadenas. Una vez procesadas dichas lines podemos redireccionarlas mediante una tubería al shell y ejecutarlas como comandos.

Siguiendo con nuestro ejemplo si quisieramos mover los ficheros encontrados a otra ubicación diferente en nuestro sistema de ficheros podríamos hacerlo con la siguientes linea:

find . -type f -iname *.avi | awk '{print "mv","x22"$0"x22","-t /home/user/videos/"}' | sh

Como vemos simplemente hemos unido la salida estandar (STDOUT) de find con la entrada estandar (STDIN) de awk para procesar cada linea.

Vamos a analizar cada parte del comando awk.

awk '{print "mv","x22"$0"x22","-t /home/user/videos/"}'

Tal y como hemos dicho anteriormente awk se basa en el procesado de expresiones regulares, sin embargo en esta ocasión no lo necesitaremos dado que con el comando find ya obtenemos la cadena completa con la que operar. Para ello utilizamos la variable $0. Esta variable recoge la cadena completa que llega por la tubería desde el comando find y contiene la ruta completa del fichero.

Entre {} aparece el conjunto de instrucciones a ejecutar para cada cadena. La instrucción print, a la cual anteponemos el comando «mv» para mover el fichero, seguido de una , (coma) que en el procesado de la cadena se interpreta como un espacio en blanco. A continuación incluimos un carácter en hexadecimal «x22», en este caso las comillas dobles, antes y después de la variable $0.

Es muy importante que incluyamos comillas para contener la ruta de los ficheros, pues en caso de que contengan espacios podrían no ser interpretados correctamente y fallar en la operación de copiado. Copiando o moviendo ficheros simplemente la operación dará un error. Pero imagina que estés realizando un rm -rf (borrado sin confirmación de rutas completas) y se trunca la cadena en la ruta de un directorio. Las consecuencias podrían ser catastróficas.

Es muy importante que incluyamos comillas para contener la ruta de los ficheros, pues en caso de que contengan espacios podrían no ser interpretados correctamente y fallar en la operación de copiado.

La última parte del comando simplemente indica el directorio de destino del comando mv en el directorio /home/user/videos/.

Con esto ya tenemos compuesto el comando mv y si ejecutas la linea hasta este punto obtendrás por pantalla el volcado de lineas del comando, pero no las ejecutarás. Lo cual es muy útil para revisar una última vez las operaciones a realizar antes de ejecutarlas..

Una vez seguro de la operación puedes redireccionar la salida de awk al shell con | sh y ahora sí, ejecutar las lineas de comando secuencialmente.

Uso de xargs

El comando xargs es mucho más directo. De igual modo podemos utilizar operaciones de sustitución sobre la cadena que pasamos por la tubería, pero como con awk no necesitamos realizar ninguna operación en este momento. Pasaremos la cadena sin modificar.

find . -type f -iname *.avi | xargs -I '{}' mv {} -t /home/user/videos/

Igual que con awk puedes realizar una vista previa de cada linea con xargs -p que activa el modo interactivo solicitando confirmación antes de lanzar un proceso y mostrando el comando resultante en la consola.

Empleo de find -exec

El propio comando find permite lanzar otros comandos sin tener que realizar una tubería. Su funcionamiento es muy sencillo. Por medio de la cadena {} pasaremos el patrón localizado con find y lo anexará al comando tras la inclusión del atributo -exec. De este modo podemos realizar la misma operación de ejemplos anteriores con la siguiente sintaxis:

find . -type f -iname *.avi -exec mv {} -t /home/user/videos/ ;

Debeis de aseguraros de finalizar el comando después del parámetro -exec con el delimitador de final de comando ; (punto y coma) escapado.

Gracias Ángel por el aporte.

Últimos consejos

Efectivamente ambos comandos son muy similares y como siempre en Linux existen muchos caminos para llegar al mismo sitio. Me gustaría terminar con un par de consejos:

  • Se precavido. Hasta el más experimentado puede equivocarse, así que antes de lanzar el comando es mejor que te asegures que la sintaxis de las lineas de comando es correcta.
  • Xargs es un programa desarrollado exclusivamente para redireccionar la salida de comandos al STDIN de otros por lo que tiene algunas ventajas muy interesantes como por ejemplo poder lanzar varias tareas a la vez. Mientras con awk ejecutamos cada linea secuencialmente con xargs -P podremos especificar cuantos procesos queremos simultáneamente. Utilízalo en procesos largos para ganar tiempo.

Espero que os haya resultado interesante y espero vuestros aportes sobre otros métodos que utiliceis.

Fuentes

http://en.wikipedia.org/wiki/AWK
http://www.gnu.org/software/gawk/manual/gawk.html
http://linux.die.net/man/1/xargs

Comparte si te ha gustado

Autor:
Última actualización:

4 comentarios

  1. En vez de esto: find . -type f -iname *.avi -exec mv {} -t /home/user/videos/ ;
    Seria así: find . -type f -iname *.avi -exec mv -t /home/user/videos/ {} \;

    Lo he usado concreta-mente para hacer esto: find ./ -mtime +60 -exec mv -t /carpeta/ {} \;

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

De acuerdo con lo dispuesto en el Reglamento (UE) 2016/679 de 27 de abril de 2016, consiento que mis datos sean tratados bajo la responsabilidad de Oscar Gascón Arjol para recibir respuesta a consultas. publicación de comentarios del blog y que las conserve mientras haya un interés mutuo para ello. Me doy por informado que tengo derecho a revocar este consentimiento en cualquier momento y a ejercer los de acceso, rectificación, portabilidad y supresión de mis datos y los de limitación y oposición al tratamiento dirigiéndome por email a me@oscargascon.es. También estoy informado de que puedo reclamar ante la autoridad de control a www.agpd.es.