¿Cómo copiar una carpeta de forma recursiva de forma idempotente usando cp?
On diciembre 1, 2020 by adminCuando uso
cp -R inputFolder outputFolder
, el resultado es dependiente del contexto :
- si
outputFolder
no existe, creará, y la ruta de la carpeta clonada seráoutputFolder
. - si
outputFolder
existe, entonces el clon creado beoutputFolder/inputFolder
Esto es horrible , porque quiero crear un script de instalación, y si el usuario lo ejecuta dos veces por error, habrá creado outputFolder
la primera vez, luego en la segunda ejecución todo ser creado una vez más en outputFolder/inputFolder
.
- Quiero siempre el primer comportamiento: crear un clon junto al original (como hermano).
- Quiero usar
cp
para ser portátil (p. ej. MINGW norsync
enviado) - Revisé
cp -R --parents
pero esto recrea la ruta hasta el final del árbol de directorios (por lo que el clon no seráoutputFolder
sinosome/path/outputFolder
) -
--remove-destination
o--update
en caso de que 2 no cambie nada, aún así, las cosas se copian enoutputFolder/inputFolder
Es hay una manera de hacer esto sin verificar primero la existencia de outputFolder
(si la carpeta no existe, entonces …) o usando rm -rf outputFolder
?
¿Cuál es la forma portátil y acordada de UNIX de hacer eso?
Comentarios
Responder
Use esto en su lugar:
cp -R inputFolder/. outputFolder
Esto funciona exactamente de la misma manera que, digamos, cp -R aaa/bbb ccc
funciona: si ccc
no «existe, entonces» s creado como una copia de bbb
y su contenido; pero si ccc
ya existe, entonces ccc/bbb
se crea como la copia de bbb
y su contenido .
Para casi cualquier instancia de bbb
, esto da el comportamiento indeseable que anotó en su Pregunta. Sin embargo, en esta situación específica, bbb
es solo .
, por lo que aaa/bbb
es realmente aaa/.
, que a su vez es solo aaa
pero con otro nombre. Entonces tenemos estos dos escenarios:
-
ccc
no existe:El comando
cp -R aaa/. ccc
significa» crearccc
y copiar el contenido deaaa/.
enccc/.
, es decir, copieaaa
enccc
. -
ccc
sí existe:El comando
cp -R aaa/. ccc
significa» copiar el contenido deaaa/.
enccc/.
, es decir, copieaaa
enccc
.
Comentarios
- Eh, eso ‘ es nuevo para mí, ¡pero parece funcionar! ¿Está esto documentado en alguna parte?
- Yo ‘ he probado
inputFolder/.
en lugar deinputFolder
y de hecho me funciona en Windows / Git Bash. (Sin embargo, no funciona con solo un/
final, también necesito el punto después de la barra).Di +1 porque es ‘ interesante, aunque ‘ probablemente sea un poco complicado para usarlo en código público:) - @IlmariJaronen no ‘ no necesita ser documentado explícitamente. Siga la explicación y ‘ verá cómo simplemente » sigue las reglas » .
Responder
No copie la carpeta, solo copie el contenido:
## Create the target directory. The -p suppresses error messages ## if the directory already exists mkdir -p outputFolder ## Copy the contents recursively, this will not recreate the parent cp -R inputfolder/* outputfolder/
De esta manera, ambos se aseguran de que el directorio de destino se cree la primera vez que se ejecuta el script y evita problema al ejecutarlo por segunda vez.
Chris Down señala muy correctamente que en bash, esto omitirá los archivos cuyo nombre comience con un .
. Para evitar esto , puede ejecutar shopt -s dotglob
antes de ejecutar el comando anterior.
Ambos -p
para mkdir
y -R
para cp
están definidos por POSIX, por lo que debería ser perfectamente portátil.
Comentarios
- Nota: esto ganó ‘ t archivos de copia que comienzan con
.
. En bash, puede usardotglob
para eso. - Tenga en cuenta que es probable que este método falle si el número de entradas de directorio en inputfolder es grande, porque la expansión glob podría exceder la longitud máxima de la línea de comando.
Respuesta
Pruebe el -T
opción a cp
. Esto existe en GNU coreutils cp
versión 8.22; puede que no sea portátil fuera de eso.
Comentarios
- Sí, parece que esto es exactamente lo que quería:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Lamentablemente, mi versión decp
es mucho más antiguo. - +1 Lo usé hoy.
-u
es una excelente opción para agregar y hacerlo perezoso.
Responder
También puede usar rsync
:
rsync -uav inputFolder/ outputFolder/
(observe las barras, especialmente después de la primera )
Respuesta
Puede usar la opción -t
de cp
comando como:
cp -R inputFolder -t outputFolder
ahora, si la carpeta de destino no existe, arrojará un error:
cp: failed to access ‘outputFolder’: No such file or directory
nota que el comando anterior copiará inputFolder
junto con su contenido (y no solo el contenido dentro de él)
si lo desea copiar solo el contenido de inputFolder
se vuelve un poco complicado (ya que debe tener cuidado al usar shell globbing mientras usa asterisco *)
cp -R -t outputFolder/ -- inputFolder/*
ahora, si la carpeta de destino no existe, arrojará un error:
cp: failed to access ‘outputFolder’: No such file or directory
funciona con cp (GNU coreutils) 8.23
Respuesta
En mi opinión, no hay nada más simple y portátil que rm -rf outputFolder
. Entonces, siempre me quedo con eso. Entiendo que tu pregunta es diferente, pero creo que esta es la mejor práctica.
Comentarios
- Eso falla si había permisos o propiedades específicas en el objetivo que necesitaba mantenerse. O si fuera un enlace simbólico.
- La pregunta quiere que el resultado de
cp
sea el mismo, tanto cuando el directorio no existe como cuando existe. Entonces, ¿de qué estás hablando? - El comando
cp
dado por el OP no ‘ t mantiene los permisos (o marcas de tiempo).
mv
mueva una colección de archivos a uno normal?rsync
?cp
, conectar dostar
comandos es una buena forma confiable de copiar árboles.rsync
enviado.tar -C input -cf - . | tar -C output -xf -
funciona.-C
cambia el directorio de trabajo, pero no es una opción estándar, por lo que si ‘ no es compatible, debe ejecutar los dos archivos tar en los directorios de trabajo correctos para empezar, por ejemplo( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Sin embargo, si ‘ estás trabajando con Windows, solo usaría la respuesta de roaima ‘ .