*nix One-Liner: busque archivos de forma recursiva por extensión y cópielos en un nuevo directorio con el nombre de la extensión

*nix One-Liner: busque archivos de forma recursiva por extensión y cópielos en un nuevo directorio con el nombre de la extensión

Dados muchos directorios/subdirectorios que contienen muchos tipos de archivos diferentes (csv, txt, jpg, etc.), ¿qué comando buscará en cada directorio de forma recursiva y copiará los archivos en un nuevo directorio de nivel superior con el nombre de cada extensión?

¿Hay alguna manera de lograr esto con una sola línea?

Respuesta1

Solución

El siguiente comando enumerará todos los archivos en el directorio actual ( .) y los moverá a directorios con el nombre de sus extensiones en ./OUTPUT:

for file in $(find . -type f) ; do EXTENSION=$(echo "$file" |sed 's/.*\.//g') ; rsync -av $file ./OUTPUT/$EXTENSION/ ; done

Antes de ejecutar el comando anterior, deberá:

  • Cree el directorio OUTPUT en el directorio actual ( mkdir OUTPUT)
  • Asegúrese de que no haya archivos con nombres coincidentes en ninguno de los subdirectorios

Explicación

En primer lugar, el bucle asigna a la variable fileun archivo de la salida de find . -type f. A continuación se muestra un ejemplo de salida del findcomando:

./a/myfile1.zip
./a/b/adoc1.txt
./a/b/adoc2.txt
./a/b/adoc3.txt
./a/b/adoc4.txt
./a/myfile3.zip
./a/myfile4.zip
./a/myfile2.zip

La variable EXTENSIONse crea y su valor es la filevariable con todo hasta el último punto eliminado. Así ./a/myfile1.zipsería zip.

Luego, el archivo se sincroniza (con detalle, v) con un directorio que lleva OUTPUTel nombre de la extensión del archivo.

Un ejemplo de mi árbol de archivos después de ejecutar este comando en alguna prueba:

./a
./a/b
./a/b/adoc1.txt
./a/b/adoc2.txt
./a/b/adoc3.txt
./a/b/adoc4.txt
./a/b/c
./a/myfile1.zip
./a/myfile2.zip
./a/myfile3.zip
./a/myfile4.zip
./b
./b/c
./b/c/d
./b/c/file1.txt
./b/c/file2.txt
./b/c/file3.txt
./b/c/file4.txt
./c
./c/d
./c/d/e
./c/d/e/afile1.rar
./c/d/e/afile2.rar
./c/d/e/afile3.rar
./c/d/e/afile4.rar
./OUTPUT
./OUTPUT/rar
./OUTPUT/rar/afile1.rar
./OUTPUT/rar/afile2.rar
./OUTPUT/rar/afile3.rar
./OUTPUT/rar/afile4.rar
./OUTPUT/txt
./OUTPUT/txt/adoc1.txt
./OUTPUT/txt/adoc2.txt
./OUTPUT/txt/adoc3.txt
./OUTPUT/txt/adoc4.txt
./OUTPUT/txt/file1.txt
./OUTPUT/txt/file2.txt
./OUTPUT/txt/file3.txt
./OUTPUT/txt/file4.txt
./OUTPUT/zip
./OUTPUT/zip/myfile1.zip
./OUTPUT/zip/myfile2.zip
./OUTPUT/zip/myfile3.zip
./OUTPUT/zip/myfile4.zip

Respuesta2

Esto se puede hacer de forma muy sencilla conencontrar, como lo sugirió correctamente Alex: asumiendo que el directorio para buscar archivos es/ruta1/a/algún/diry desea mover los archivos a/ruta2/a/otro/dir/txtetcétera,

find /path1/some/dir -type f -exec bash -c 'MYDIR=/path2/to/other/dir${0##*.}; [ -d $MYDIR ] && mv $0 $MYDIR  || mv $0 /path3/to/miscellanea' {} \; 

Este comando primero busca todos los archivos (-tipo f) contenido dentro del directorio/ruta1/a/algún/diry todos sus subdirectorios, luego pasa el nombre de cada uno de esos archivos ({}) a un pequeño bash de dos líneas, que primero extrae la extensión del nombre del archivo (denominado$0dentro de bash) y lo asigna a la variable de shell localMYDIR, entoncespruebaspara la existencia de un directorio adecuado (si no existe dicho directorio, entonces tal vez cometimos un error), y si el directorio existe (&&...) mueve el archivo al directorio apropiado; de lo contrario (||...) coloca el archivo en un lugar predeterminado,/ruta3/a/miscelánea.

Lo anterior supone que ya ha creado los directorios necesarios,/ruta2/a/otro/dir/txtetcétera. Si por el contrario deseas crearlos sobre la marcha, tendrás que modificar lo anterior como:

find /path1/some/dir -type f -exec bash -c 'MYDIR=/path2/to/other/dir/${0##*.}; mkdir -p $MYDIR && mv $0 $MYDIR' {} \; 

información relacionada