Fusionar carpetas con mv?

149

Si uso mvpara mover una carpeta llamada "carpeta" a un directorio que ya contiene "carpeta", ¿se fusionarán o se reemplazarán?

Dominique
fuente

Respuestas:

120

mvno puede fusionar o sobrescribir directorios, fallará con el mensaje "mv: no se puede mover 'a' a 'b': el directorio no está vacío" , incluso cuando está utilizando la --forceopción.


Puede solucionar esto utilizando otras herramientas (como rsync, findo incluso cp), pero debe considerar cuidadosamente las implicaciones:

  • rsyncpuede fusionar el contenido de un directorio en otro (idealmente con la opción --remove-source-files1 para eliminar de forma segura solo aquellos archivos fuente que se transfirieron con éxito, y con la opción habitual de conservación de permisos / propiedad / tiempo -asi lo desea)
    ... pero esta es una operación de copia completa y, por lo tanto, puede requerir mucho disco.
  • Opción actualmente preferida: puede combinar rsyncla --link-dest=DIRopción (para crear enlaces duros en lugar de copiar el contenido del archivo, cuando sea posible) y --remove-source-filesobtener una semántica muy similar a una regular mv.
    Para esto, --link-destse le debe dar una ruta absoluta al directorio de origen (o una ruta relativa desde el destino a la fuente ).
    ... pero esto se usa --link-destde forma no intencionada (lo que puede o no causar complicaciones), requiere conocer (o determinar) la ruta absoluta a la fuente (como argumento para --link-dest), y nuevamente deja una estructura de directorio vacía para limpiar como por 1 .
  • Puede usarfind para recrear secuencialmente la estructura del directorio fuente en el destino, luego mover individualmente los archivos reales
    ... pero esto tiene que repetirse a través de la fuente varias veces y puede encontrar condiciones de carrera (se crean nuevos directorios en la fuente durante el proceso de varios pasos) )
  • cppuede crear enlaces duros (simplemente, punteros adicionales al mismo archivo existente), lo que crea un resultado muy similar a una fusión mv(y es muy eficiente en IO ya que solo se crean punteros y no hay que copiar datos reales)
    ... pero esto nuevamente sufre de una posible condición de carrera (los archivos nuevos en la fuente se eliminan aunque no se copiaron en el paso anterior)

Cuál de estas soluciones (si las hay) es apropiada dependerá en gran medida de su caso de uso específico.
Como siempre, piense antes de ejecutar cualquiera de estos comandos y realice copias de seguridad.


1: Tenga en cuenta que rsync --remove-source-filesno eliminará ningún directorio, por lo que tendrá que hacer algo como find -depth -type d -empty -deletedespués para deshacerse del árbol de directorios de origen vacío.

n.st
fuente
Parece que has probado solo una implementación de mv. Esta respuesta sería mejor con una verdad más amplia. Linux, BSD y Unix "real", o una referencia de POSIX o SUS.
Warren Young
@WarrenYoung Tienes razón, solo probé la mvimplementación utilizada por Debian - el énfasis está en probar , ya que la página de manual no menciona este comportamiento ...
n
38
La desventaja de rsync es que en realidad copia los datos, en lugar de simplemente cambiar el enlace duro, que potencialmente requiere muchos recursos si se trata de una gran cantidad de datos.
Jonathan Mayer
77
@Keith Tenga en cuenta que --deletesolo elimina archivos en el directorio de destino que no existen en el directorio de origen.
n.st
1
@JonathanMayer rsync como varias funciones relacionadas con enlaces duros. Por ejemplo, puede preservar los enlaces duros con la -Hfunción o puede vincular archivos en el destino utilizando --link-dest. Sin embargo, vea la página de manual antes de usarlos.
allo
87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/
fazie
fuente
¿Esto eliminará los archivos de origen como en el comentario de n.st?
Dominique
3
No, preferiría hacerlo en dos pasos por razones de seguridad. La fuente fusionada y eliminada es irreversible. También se necesita un paso adicional en n.st anwer (para eliminar directorios).
fazie
3
--remove-source-filestiene la ventaja de eliminar solo los archivos que se transfirieron con éxito, por lo que puede usarlos findpara eliminar directorios vacíos y quedará con todo lo que no se transfirió sin tener que verificar rsyncla salida.
n
44
Pero en realidad no se está moviendo: el impacto en la velocidad es enorme si se trata de archivos grandes.
Alex
Pero no puede realizar movimientos puros y fusionarse en realidad.
fazie
64

Puede usar la -lopción del comando cp , que crea enlaces duros de archivos en el mismo sistema de archivos en lugar de copias de datos completos. El siguiente comando copia la carpeta source/foldera una carpeta principal ( destination) que ya contiene un directorio con el nombre folder.

cp -rl source/folder destination
rm -r source/folder

También es posible que desee utilizar el -P( --no-dereference- no elimine la referencia de los enlaces simbólicos) o -a( --archive- conservar todos los metadatos, también incluye la -Popción), según sus necesidades.

palswim
fuente
77
@rautamiekka: Supongo que está preguntando la razón para usar enlaces duros. Si no sabe qué son los enlaces duros y por qué debería usarlos, entonces probablemente no debería tomar esta ruta. Sin embargo, la creación de enlaces duros no hace una copia completa, por lo que esta operación tomaría órdenes de magnitud menos tiempo que una copia completa. Y usaría enlaces duros en lugar de enlaces blandos para poder eliminar los archivos de origen y aún tener los datos correctos en lugar de punteros a rutas no válidas. Y en cplugar de hacerlo, rsyncya que todos los sistemas tienen cpy todos están familiarizados con él.
palswim
77
La brillantez de esta solución puede pasarse por alto por no ser la respuesta aceptada. Es una solución elegante. Obtiene la capacidad de fusión cpcon el tiempo de operación de mv.
TheHerk
2
si sabe que no necesita mover archivos que ya existen en el destino que también desea agregar-n
ndemou
1
@Ruslan: Esto es cierto, pero no puede moverse sin una copia a través de los sistemas de archivos con ningún método. Incluso mv /fs1/file /fs2/(en todos los sistemas de archivos) realizará una copia y luego una eliminación.
palswim
2
Correcto, pero si bien mvfuncionará (siempre que el directorio de destino aún no exista), incluso si no es "eficiente" o como se llame, cp -rlfallará.
Ruslan
23

Recomiendo estos cuatro pasos:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

o mejor aún, aquí hay un script que implementa una semántica similar a mv:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in ${@:1:$((${#@} -1))}; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done
Jonathan Mayer
fuente
Args son FUENTE, DEST
schuess
Esto se ve muy útil. Estoy tentado de usarlo para limpiar mi disco duro. ¿Puede algún otro experto comentarlo antes de confiar un montón de copias de seguridad a este script? :-)
LarsH
Por cierto, si desea hacer el equivalente de rsync -u(solo actualizar si es más reciente), mv(en algunas versiones al menos) también puede tomar la -uopción. Sin embargo, en ese caso, es posible que desee eliminar los directorios de origen no vacíos, así como los vacíos, para cubrir los casos en que los archivos en el árbol de origen no son más nuevos. @schuess: Parece que puede haber múltiples argumentos SOURCE, si lo necesita.
LarsH
1
Esto no maneja bien los espacios en blanco. Lo probé con algunos directorios con espacios en ellos y terminé con una serie infinita de directorios anidados.
rofer
16

Aquí hay una forma de combinar los directorios. Es mucho más rápido que rsync ya que solo cambia el nombre de los archivos en lugar de copiarlos y luego eliminarlos.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'
Joya
fuente
Eso es interesante pero solo vagamente relevante para el tema y ni siquiera remotamente lo que el usuario preguntó.
Shadur
17
En realidad, el código de Jewel hace exactamente lo que el usuario solicitó, con la excepción de crear directorios faltantes. Tal vez deberías mirar de nuevo?
Jonathan Mayer
3
Agregaría para usar "-print0" en find y "-0" en xargs porque hay archivos que tienen espacios en los nombres. Además, hay un pequeño problema, si un nombre contiene paréntesis, no se van a mover.
markuz
2
Esto es mucho más rápido que rsync para una pequeña cantidad de archivos, pero bifurca un nuevo proceso para cada archivo, por lo tanto, el rendimiento es abismal con una gran cantidad de archivos pequeños. La respuesta de @palswim no sufre este problema.
b0fh
2
El comando fallará si in destya es un directorio con el mismo nombre que in source. Y los archivos se moverán a un dest, que está en source. El comando no hace más quemv source/* source/dest/.
ceder el
3

Una forma de lograr esto sería usar:

mv folder/* directory/folder/
rmdir folder

Siempre que no haya dos archivos con el mismo nombre foldery directory/folder, obtendrá el mismo resultado, es decir, la fusión.

Asheeshr
fuente
3
¿Cómo funciona exactamente rm folder?
JakeGould
55
@JakeGould No, en absoluto. :)
n
rm folder -fRsiempre funciona para mí
Octopus
2
Tenga en cuenta que esto no funcionará para los archivos ocultos
b0fh
2

Para las copias más puras, uso el método tar (-) B blockread copy.

ejemplo, desde dentro de la ruta de origen ('cd' allí si es necesario):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

Esto crea una copia exacta del árbol de origen, CON el propietario y los permisos intactos. Y si la carpeta de destino existe, los datos se fusionarán. Solo se sobrescribirán los archivos que ya existen.

Ejemplo:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

Cuando la acción de copiar es exitosa, puede eliminar la fuente ( rm -rf <source>). Por supuesto, este no es un movimiento exacto: los datos se copiarán hasta que elimine la fuente.

Como opción, puede ser detallado (mostrar en pantalla el archivo que se está copiando), con -v: tar cBvf -

  • c: crear
  • B: leer bloque completo (para lectura de tubería)
  • v: detallado
  • f: archivo para escribir
  • x: extracto
  • -: stdout / stdin

sourcefoldertambién puede ser *(para cualquier cosa en la carpeta actual)

gjs
fuente
Especificar f -tar generalmente no es necesario: el valor predeterminado es leer desde stdin / escribir en stdout.
muru
1

Aquí hay un guión que funcionó para mí. Prefiero mv sobre rsync, así que uso las soluciones de Jewel y Jonathan Mayer.

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd $SRC;
    find . -type d -exec mkdir -p ${DEST}/{} \;
    find . -type f -exec mv {} ${DEST}/{} \;
    find . -type d -empty -delete
    popd
done
xer0x
fuente
Esta solución no escapa correctamente los nombres de ruta, tenga cuidado.
usuario12439
@ user12439, actualizaré la solución si me muestra qué parte corregir.
xer0x
1

No es una buena idea usar comandos como cp o rsync. Para archivos grandes, llevará mucho tiempo. mv es mucho más rápido ya que solo actualiza los inodes sin copiar físicamente los archivos. Una mejor opción es usar el administrador de archivos de su sistema operativo. Para Opensuse, hay un administrador de archivos llamado Konquerer. Puede mover archivos sin copiarlos realmente. Tiene la función "cortar y pegar" como en Windows. Simplemente seleccione todos los subdirectorios en el directorio A. Haga clic derecho y "muévase" al directorio B que puede contener subdirectorios con los mismos nombres. Los fusionará. También hay opciones si desea sobrescribir o renombrar archivos con el mismo nombre.

simon
fuente
1
OP pregunta qué sucede cuando mvse usa.
don_crissti
0

Solución Python

Como no pude encontrar una solución preexistente satisfactoria, decidí hacer un script Python rápido para lograrlo.

En particular, este método es eficiente porque solo recorre el árbol de archivos de origen una vez de abajo hacia arriba.

También le permitirá ajustar rápidamente cosas como el manejo de sobrescritura de archivos a su gusto.

Uso:

move-merge-dirs src/ dest/

moverá todo el contenido src/*a dest/y src/desaparecerá.

mover-fusionar-directorios

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub aguas arriba .

Ver también: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
fuente
0

Este es el comando para mover archivos y carpetas a otro destino:

$ mv /source/path/folder /target/destination/

Recuerde : el mvcomando no funcionará si la carpeta es b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (es decir, ya existe otra carpeta con el mismo nombre en el destino) y d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: no se puede mover '/ source / path / folder' a '/ target / destination / folder': el directorio no está vacío

Si la carpeta de destino está vacía, el comando anterior funcionará bien.

Entonces, para fusionar ambas carpetas en cualquier caso,
hágalo en 2 comandos:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

O combine ambos como un comando de una sola vez:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv = mover
cp = copiar
rm = eliminar

-r para directorio (carpeta)
-f forzar ejecución

Talha Siddiqi
fuente