¿Cómo sobrescribir archivos de destino con mv?

155

Tengo un montón de archivos y directorios en un subdirectorio que quiero mover al directorio principal. Ya hay algunos archivos y directorios en el directorio de destino que deben sobrescribirse. Los archivos que solo están presentes en el destino deben dejarse intactos. ¿Puedo obligarme mva hacer eso? ( mv * ..) Se queja

mv: cannot move `xyz' to `../xyz': Directory not empty

¿Qué me estoy perdiendo?

EricSchaefer
fuente
3
¿Lo has intentado mv -f?
sakisk
Me pregunto por qué mv -fno es la respuesta correcta.
Pedro Lobito
@PedroLobito: ¿Porque no funciona? -f solo sobrescribe archivos, pero no funciona si mueve subdirectorios que existen en el destino y no están vacíos.
EricSchaefer

Respuestas:

118

Deberá copiarlos en el destino y luego eliminar la fuente, utilizando los comandos cp -r * ..seguidos de rm -rf *.

No creo que pueda "fusionar" directorios usando mv.

dogbane
fuente
44
Bueno, eso es lo que no quería hacer, porque tomará mucho tiempo ... Gracias de todos modos.
EricSchaefer
12
¿Presumiblemente mves más rápido porque estás en el mismo sistema de archivos? ¿Qué cp -lsucede si usas para crear enlaces duros en lugar de mover realmente los archivos?
mattdm
66
Debe usar en cp -alugar de cp -rpara preservar los atributos del archivo (marca de tiempo, permisos, etc.).
dotancohen
3
Para las personas que llegan tarde a través de google, la respuesta a continuación por @palswim emula el comportamiento de mv creando nuevos enlaces duros a los datos y luego eliminando los enlaces antiguos. Respuesta corta cp -rl source destination && rm -r source.
William Everett
72

rsyncprobablemente sería una mejor opción aquí. Es tan simple como rsync -a subdir/ ./.

Mi árbol de prueba en filename: contentsformato:

./file1:root
./file2:root
./dir/file3:dir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Corriendo rsync:

$ rsync -a -v subdir/ ./
sending incremental file list
./
file1
dir/
dir/file3

Da:

./file1:subdir
./file2:root
./dir/file3:subdir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Y luego, para emular mv, probablemente desee eliminar el directorio fuente:

$ rm -r subdir/

Dando:

./file1:subdir
./file2:parent
./dir/file3:subdir
./dir/file4:dir

Si esto está mal, ¿puede proporcionar un ejemplo similar (por ejemplo, usando mi árbol de prueba cerca de la parte superior de esta respuesta) con el resultado deseado?

Mikel
fuente
1
rsync copias. Esta pregunta es acerca de mudarse.
Gilles
1
@Gilles: Gracias. Agregué rm -ral final para que sea básicamente lo mismo que mv.
Mikel
3
copy-then-delete no es equivalente a mv cuando el origen y el destino están en el mismo sistema de archivos. mves atómico, conserva los números de inodo (para que el archivo pueda permanecer abierto) y no requiere tiempo ni espacio para hacer una copia.
Gilles
55
@Gilles: Me doy cuenta de eso, pero actualmente la respuesta principal es cp -r; rm -r. Creo que en ese sentido, rsyncvale la pena mencionar también.
Mikel
Ya lo hice con cp / rm (era urgente). Realmente tomó mucho tiempo. El guión de Gilles probablemente habría sido mucho más rápido, pero también fue demasiado tarde.
EricSchaefer
47

rsyncpuede eliminar la fuente después de las copias con el --remove-source-filesparámetro Esta debería ser una manera conveniente de hacer lo que le gustaría.

De la rsync man page:

        --remove-source-files   sender removes synchronized files (non-dir)
Mike Chen
fuente
Esta es realmente la mejor respuesta. No pude usar cp -r; rmpor falta de espacio libre. En cambio rsync --remove-source-files, el espacio en disco usado minimizado evitó copiar sobre los mismos archivos exactos.
guaka
20

Puede hacer esto con cpy rm, pero sin copiar la gran cantidad de datos que intenta (presumiblemente) está tratando de evitar la transferencia. @mattdm aludió a esto en su comentario , y una respuesta para otra pregunta tiene una discusión más completa sobre varias opciones.

cp -rlf source destination
rm -r source

Esencialmente, la -lopción para el cpcomando crea enlaces duros a los archivos en lugar de copiar sus datos a archivos nuevos.

palswim
fuente
1
Sé que el OP ya completó su tarea que provocó la pregunta, pero espero que esta respuesta pueda ayudar a cualquiera con este problema en el futuro.
palswim
Esta es realmente la respuesta que necesitaban. Acabo de hacer esto con 60 GB de miles de pequeños archivos de correo electrónico Cyrus y me tomó solo 21 segundos.
labradort
Esta es también la ruta que tomé: simplemente la cambié cp -al source destinationpara preservar la información y los permisos del propietario.
piit79
Creo que para hacer lo que OP me solicitó, debe agregar la opción -f; de lo contrario, no se sobrescribirá si el archivo existe. ¿Puedes confirmar eso o estoy haciendo algo mal?
das Keks
@dasKeks: en realidad, se cpsobrescribe de forma predeterminada. La -fopción intenta eliminar el archivo (como en rm) antes de intentar copiar de nuevo, lo que puede ayudar si el proceso no puede abrir el archivo para escribir, aunque algunas personas prefieren ver que hubo un error. No sé si le importó al OP, pero de -ftodos modos agregué la bandera a mi respuesta.
palswim
8

Aquí hay un script que mueve los archivos de debajo /path/to/source/roota la ruta correspondiente debajo /path/to/destination/root.

  • Si existe un directorio tanto en el origen como en el destino, los contenidos se mueven y fusionan de forma recursiva.
  • Si existe un archivo o directorio en el origen pero no en el destino, se mueve.
  • Cualquier archivo o directorio que ya exista en el destino se queda atrás. (En particular, los directorios combinados se quedan atrás en la fuente. Esto no es fácil de solucionar).

Cuidado, código no probado.

export dest='/path/to/destination/root'
cd /path/to/source/root
find . -type d \( -exec sh -c '[ -d "$dest/$0" ]' \; -o \
                  -exec sh -c 'mv "$0" "$dest/$0"' {} \; -prune \) \
    -o -exec sh -c '
        if ! [ -e "$dest/$0" ]; then
          mv -f "$0" "$dest/$0"
        fi
' {} \;
Gilles
fuente
Dos correcciones: necesita un \;antes -ode la primera línea del findcomando, y no debe escapar !en el if- es !, no\!
llhuii
2

Este hilo está disponible desde hace años y todavía ocupa el puesto número 1 en Google, así que quería agregar otro método. Cómo suelo hacer esto: empaquetar el contenido del subdirectorio en un tarball, mover el tarball al directorio principal y luego extraerlo con el comportamiento predeterminado de sobrescritura. Esto hace exactamente lo que estás buscando. Luego puedes eliminar tu subdirectorio.

cd xyz
tar -cvzpf tmp.tar.gz *
mv tmp.tar.gz ../tmp.tar.gz
cd ..
tar -xvzpf tmp.tar.gz
rm -rf xyz
rm -f tmp.tar.gz
Simon Kraus
fuente
Esto solo funciona si tiene el espacio extra para el archivo tar comprimido. ¿Y realmente desea mantener el archivo tar temporal después de la extracción?
Anthon
Código editado para eliminar el archivo tmp. Hoy en día el espacio no es el problema en la mayoría de los casos. #terabyteages
Simon Kraus
0

Si tiene suficiente almacenamiento, puede hacerlo de la siguiente manera:

mv -bfv directory_1/* directory_2/ # all duplicate source files/directories 
                                   # will have ~ appended to them
find -name "*~" -delete            # will recursively find and delete all files 
                                   # with ~ on the end

Asegúrese de que no haya ningún archivo importante con un ~ al final, pero si lo hay, puede agregarlo en --suffix=whateveryouwantlugar del predeterminado.

4D3H2
fuente