Pregunta

En el shell bash de UNIX (específicamente Mac OS X Leopard), ¿cuál sería la forma más sencilla de copiar cada archivo que tenga una extensión específica de una jerarquía de carpetas (incluidos los subdirectorios) a la misma carpeta de destino (sin subcarpetas)?

Obviamente existe el problema de tener duplicados en la jerarquía de fuentes.No me importaría si se sobrescriben.

Ejemplo:Necesito copiar cada archivo .txt en la siguiente jerarquía

/foo/a.txt
/foo/x.jpg
/foo/bar/a.txt
/foo/bar/c.jpg
/foo/bar/b.txt

A una carpeta llamada 'dest' y obtenga:

/dest/a.txt
/dest/b.txt
¿Fue útil?

Solución

En fiesta:

find /foo -iname '*.txt' -exec cp \{\} /dest/ \;

find encontrará todos los archivos en la ruta /foo coincidente con el comodín *.txt, sin distinguir entre mayúsculas y minúsculas (eso es lo que -iname medio).Para cada archivo, find ejecutará cp {} /dest/, con el archivo encontrado en lugar de {}.

Otros consejos

El único problema con la solución de Magnus es que crea un nuevo proceso "cp" para cada archivo, lo cual no es muy eficiente, especialmente si hay una gran cantidad de archivos.

En Linux (u otros sistemas con GNU coreutils) puedes hacer:

find . -name "*.xml" -print0 | xargs -0 echo cp -t a

(El -0 le permite funcionar cuando los nombres de sus archivos tienen caracteres extraños, como espacios,).

Desafortunadamente, creo que las Mac vienen con herramientas estilo BSD.¿Alguien conoce un equivalente "estándar" al interruptor "-t"?

Las respuestas anteriores no permiten colisiones de nombres ya que al autor de la pregunta no le importaba que se sobrescribieran los archivos.

Me importa que se sobrescriban los archivos, así que se me ocurrió un enfoque diferente.Reemplazar cada / en la ruta con - mantenga la jerarquía en los nombres y coloque todos los archivos en una carpeta plana.

Usamos find para obtener la lista de todos los archivos, luego awk para crear un comando mv con el nombre de archivo original y el nombre de archivo modificado y luego los pasamos a bash para que los ejecute.

find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash

donde ./from y ./to son directorios desde y hacia mv.

Si realmente desea ejecutar un solo comando, ¿por qué no configurar uno y ejecutarlo?Al igual que:

$ find /foo  -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx

Pero eso no importará demasiado en cuanto al rendimiento a menos que hagas esto mucho o tengas un montón de archivos.Sin embargo, tenga cuidado con las connivencias de nombres.Al realizar pruebas, noté que GNU cp al menos advierte sobre colisiones:

cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'

Creo que lo más limpio es:

$ find /foo  -name '*.txt' | xargs -i cp {} /dest

Menos sintaxis para recordar que la opción -exec.

En lo que respecta a la página de manual para cp en un cuadro FreeBSD, no es necesario cambiar -t.cp asumirá que el último argumento en la línea de comando es el directorio de destino si se pasan más de dos nombres.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top