Comment écraser les fichiers cibles avec mv?

155

J'ai une tonne de fichiers et répertoires dans un sous-répertoire que je veux déplacer dans le répertoire parent. Il y a déjà quelques fichiers et répertoires dans le répertoire cible qui doivent être écrasés. Les fichiers uniquement présents dans la cible doivent rester intacts. Puis-je forcer mvà faire ça? Il se mv * ..plaint

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

Qu'est-ce que je rate?

EricSchaefer
la source
3
Avez-vous essayé mv -f?
Sakisk
Je me demande pourquoi mv -fla réponse n'est pas correcte.
Pedro Lobito
@ PedroLobito: Parce que ça ne marche pas? -f ne remplace que les fichiers, mais ne fonctionne pas si vous déplacez des sous-répertoires existant dans la destination et non vides.
EricSchaefer

Réponses:

118

Vous devrez les copier vers la destination, puis supprimer la source, en utilisant les commandes cp -r * ..suivies de rm -rf *.

Je ne pense pas que vous puissiez "fusionner" des répertoires en utilisant mv.

dogbane
la source
4
Eh bien, c'est ce que je ne voulais pas faire, parce que ça va prendre du temps ... Merci quand même.
EricSchaefer
12
Vraisemblablement, mvc'est plus rapide parce que vous êtes sur le même système de fichiers? Et si vous cp -lutilisiez pour créer des liens durs plutôt que de déplacer réellement les fichiers?
Mattdm
6
Vous devez utiliser à la cp -aplace de cp -rafin de préserver les attributs de fichier (horodatage, autorisations, etc.).
dotancohen
3
Pour les personnes arrivant tard via Google via Google, la réponse ci-dessous de @palswim émule le comportement de mv en créant de nouveaux liens fixes vers les données, puis en supprimant les anciens. Réponse courte cp -rl source destination && rm -r source.
William Everett
72

rsyncserait probablement une meilleure option ici. C'est aussi simple que rsync -a subdir/ ./.

Mon arbre de test dans filename: contentsFormat:

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

En cours d'exécution rsync:

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

Donne:

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

Et puis, pour émuler mv, vous voudrez probablement supprimer le répertoire source:

$ rm -r subdir/

Donnant:

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

Si cela est faux, pouvez-vous fournir un exemple similaire (par exemple, utiliser mon arbre de test situé en haut de cette réponse) avec le résultat souhaité?

Mikel
la source
1
copies rsync. Cette question concerne le déménagement.
Gilles
1
@ Gilles: Merci. J'ai ajouté rm -rà la fin pour le rendre fondamentalement identique à mv.
Mikel
3
copy-then-delete n'est pas équivalent à mv lorsque la source et la destination se trouvent sur le même système de fichiers. mvest atomique, conserve les numéros d'inode (le fichier peut rester ouvert), et ne prend pas de temps et d'espace pour en faire une copie.
Gilles
5
@ Gilles: Je m'en rends compte, mais la réponse principale est actuellement cp -r; rm -r. Je pense que dans ce sens, il rsyncconvient de mentionner aussi.
Mikel
Je l'ai déjà fait avec cp / rm (c'était urgent). Cela a vraiment pris beaucoup de temps. Le scénario de Gilles aurait probablement été beaucoup plus rapide, mais il était trop tard aussi.
EricSchaefer
48

rsyncpeut supprimer la source après les copies avec le --remove-source-filesparamètre. Cela devrait être un moyen pratique de faire ce que vous voulez.

Du rsync man page:

        --remove-source-files   sender removes synchronized files (non-dir)
Mike Chen
la source
C'est vraiment la meilleure réponse. Je ne pouvais pas utiliser à cp -r; rmcause du manque d'espace libre. Au lieu de cela, les rsync --remove-source-filesdeux espaces disque utilisés minimisés ont évité de copier exactement les mêmes fichiers.
guaka
20

Vous pouvez le faire avec cpet rm, mais sans copier l’énorme quantité de données que vous essayez (vraisemblablement) d’éviter de transférer. @mattdm a fait allusion à cela dans son commentaire , et une réponse à une autre question comporte une discussion plus complète sur diverses options.

cp -rlf source destination
rm -r source

Pour l’essentiel, l’ -loption de la cpcommande crée des liens physiques vers des fichiers plutôt que de copier leurs données dans de nouveaux fichiers.

palswim
la source
1
Je sais que le PO a déjà terminé sa tâche, ce qui a incité la question, mais j'espère que cette réponse pourra aider n'importe qui à l'avenir avec ce problème.
palswim
C'est vraiment la réponse dont ils avaient besoin. Je viens de faire cela avec 60 Go de milliers de petits fichiers de courrier électronique Cyrus et cela n'a pris que 21 secondes.
labradort
C’est également la voie que j’ai choisie - je viens de la modifier cp -al source destinationpour conserver les informations et les autorisations du propriétaire.
piit79
Je pense que pour faire ce que l'OP vous a demandé, vous devez ajouter l'option -f, sinon le fichier ne sera pas écrasé. Pouvez-vous confirmer cela ou est-ce que je fais quelque chose de mal?
das Keks
@dasKeks: En réalité, cpécrase par défaut. L' -foption tente de supprimer le fichier (comme dans rm) avant de tenter une nouvelle copie, ce qui peut aider si le processus ne peut pas ouvrir le fichier en écriture, bien que certaines personnes préfèrent voir qu'il y a une erreur. Je ne sais pas si cela importait pour le PO, mais j'ai quand même ajouté le -fdrapeau à ma réponse.
Palswim
8

Voici un script qui déplace les fichiers de dessous /path/to/source/rootvers le chemin correspondant sous /path/to/destination/root.

  • Si un répertoire existe à la fois dans la source et dans la destination, le contenu est déplacé et fusionné de manière récursive.
  • Si un fichier ou un répertoire existe dans la source mais pas dans la destination, il est déplacé.
  • Tout fichier ou répertoire qui existe déjà dans la destination est laissé. (En particulier, les répertoires fusionnés sont laissés dans le source. Ce n'est pas facile à corriger.)

Attention, code non testé.

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
la source
Deux corrections: vous avez besoin d'un \;avant la -opremière ligne de la findcommande, et vous ne devriez pas vous échapper !dans if- c'est juste !, pas\!
llhuii le
2

Ce fil existe depuis des années et occupe toujours la première place sur Google. Je voulais donc ajouter une autre méthode. Comment je fais habituellement cela: en compressant le contenu du sous-répertoire dans une archive, en déplaçant l'archive vers le répertoire parent, puis en l'extrayant avec le comportement par défaut --overwrite. Cela fait exactement ce que vous recherchez. Ensuite, vous pouvez supprimer votre sous-répertoire.

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
la source
Cela ne fonctionne que si vous disposez de l'espace supplémentaire pour le fichier tar compressé. Et voulez-vous vraiment conserver le fichier tar temporaire après l'extraction?
Anthon
Code édité pour supprimer le fichier tmp. De nos jours, l’espace n’est pas le problème dans la plupart des cas. #terabyteages
Simon Kraus
0

Si vous avez assez de stockage, vous pouvez le faire de la manière suivante:

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

Assurez-vous qu'il n'y a pas de fichiers importants avec un ~ à la fin, mais si c'est le cas, vous pouvez ajouter le fichier par --suffix=whateveryouwantdéfaut à la place.

4D3H2
la source