Fusion de dossiers avec mv?


149

Si je mvdéplace un dossier appelé "dossier" vers un répertoire contenant déjà "dossier", fusionnera-t-il ou sera-t-il remplacé?

Réponses:


120

mvne peut pas fusionner ou écraser des répertoires, il échouera avec le message "mv: impossible de déplacer" a "vers" b ": répertoire non vide" , même lorsque vous utilisez l' --forceoption.


Vous pouvez contourner ce problème en utilisant d'autres outils (tels que rsync, findou même cp), mais vous devez examiner attentivement les implications:

  • rsyncpeut fusionner le contenu d’un répertoire dans un autre (idéalement avec l’ option --remove-source-files1 de ne supprimer en toute sécurité que les fichiers source transférés avec succès, et avec l’option habituelle autorisation / propriété / conservation de temps -asi vous le souhaitez)
    mais il s’agit d’une opération de copie complète , et peut donc être très gourmand en disque.
  • Option actuellement préférée: vous pouvez combiner rsyncl' --link-dest=DIRoption (créer des liens physiques au lieu de copier le contenu du fichier, si possible) et --remove-source-filesobtenir une sémantique très similaire à une image normale mv.
    Pour cela, il --link-destfaut donner un chemin absolu au répertoire source (ou un chemin relatif de la destination à la source ).
    Mais cela utilise --link-destde manière non intentionnelle (ce qui peut causer ou non des complications), nécessite de connaître (ou de déterminer) le chemin absolu vers la source (en tant qu'argument --link-dest), et laisse à nouveau une structure de répertoire vide à nettoyer en tant que pour 1 .
  • Vous pouvez utiliserfind pour recréer séquentiellement la structure de répertoires source sur la cible, puis déplacer individuellement les fichiers réels
    mais cela doit être répété plusieurs fois à travers la source et peut rencontrer des conditions de concurrence critique (nouveaux répertoires créés à la source au cours du processus en plusieurs étapes )
  • cppeut créer des liens durs (simplement, pointeurs supplémentaires dans le même fichier existant), ce qui crée un résultat très similaire à une fusion mv(et est très IO-efficace puisque seuls les pointeurs sont créés et aucune donnée réelle doit être copié)
    ... mais ce souffre de nouveau d'une condition de concurrence possible (les nouveaux fichiers de la source sont supprimés même s'ils n'ont pas été copiés à l'étape précédente)

Laquelle de ces solutions de contournement (le cas échéant) est appropriée dépendra beaucoup de votre cas d'utilisation spécifique.
Comme toujours, réfléchissez avant d'exécuter l'une de ces commandes et effectuez des sauvegardes.


1: Notez que rsync --remove-source-filescela ne supprimera aucun répertoire, vous devrez donc faire quelque chose comme find -depth -type d -empty -deletepar la suite pour vous débarrasser de l’arborescence du répertoire source vide.


Il semble que vous ayez essayé une seule implémentation de mv. Cette réponse serait meilleure avec une vérité plus large. Linux, BSD et "réel" Unix, ou une référence de POSIX ou SUS.
Warren Young

@WarrenYoung Tu as raison, je ne ai essayé la mvmise en œuvre utilisée par Debian - l'accent étant mis sur essayé , depuis la page de manuel ne mentionne pas ce comportement ...
n.st

38
L'inconvénient de rsync est qu'il copie les données plutôt que de modifier le lien physique, ce qui peut nécessiter beaucoup de ressources si vous traitez beaucoup de données.
Jonathan Mayer

7
@Keith Notez que --deleteseuls les fichiers du répertoire de destination qui n'existent pas dans le répertoire source ne sont supprimés .
n.st

1
@JonathanMayer rsync sous forme de plusieurs fonctionnalités liées aux liens physiques. Par exemple, vous pouvez simplement conserver des liens en dur avec la -Hfonction ou vous pouvez lier des fichiers de la destination en utilisant --link-dest. Voir la page de manuel avant de les utiliser, cependant.
Allo

87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/

Est-ce que cela supprimera les fichiers source comme dans le commentaire de n.st?
Dominique

3
Non, je préférerais le faire en deux étapes pour des raisons de sécurité. La source fusionnée et supprimée est irréversible. Une étape supplémentaire dans la première requête est également nécessaire (pour supprimer des répertoires).
fazie

3
--remove-source-filesprésente l’avantage de ne supprimer que les fichiers transférés avec succès. Vous pouvez finddonc supprimer des répertoires vides et conserver tout ce qui n’a pas été transféré sans avoir à vérifier la rsyncsortie.
n.

4
Mais cela ne bouge pas réellement - l'impact sur la vitesse est énorme, si de gros fichiers sont impliqués.
Alex

Mais vous ne pouvez pas effectuer de mouvement pur et fusionner réellement.
fazie

64

Vous pouvez utiliser l' -loption de la commande cp , qui crée des liens physiques de fichiers sur le même système de fichiers au lieu de copies avec données complètes. La commande suivante copie le dossier source/folderdans un dossier parent ( destination) qui contient déjà un répertoire avec le nom folder.

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

Vous pouvez également utiliser les options -P( --no-dereference- ne pas dé-référencer les liens symboliques) ou -a( --archive- conserver toutes les métadonnées, inclut également l' -Poption), en fonction de vos besoins.


7
@rautamiekka: Je suppose que vous demandez la raison de l'utilisation de liens physiques. Si vous ne savez pas ce que sont les liens durs et pourquoi vous devriez les utiliser, vous ne devriez probablement pas emprunter cette voie. Toutefois, la création de liens physiques ne crée pas une copie complète. Cette opération prendrait donc beaucoup moins de temps qu'un ordre complet. De plus, vous utiliseriez des liens physiques plutôt que des liens symboliques pour pouvoir supprimer les fichiers source tout en conservant les données correctes au lieu de pointeurs sur des chemins non valides. Et cpplutôt que rsyncdepuis que chaque système a cpet que tout le monde est familiarisé avec elle.
Palswim

7
On peut chercher l’éclat de cette solution pour ne pas être la réponse acceptée. C'est une solution élégante. Vous obtenez la possibilité de fusion cpavec le temps de fonctionnement de mv.
TheHerk

2
si vous savez que vous n'avez pas besoin de déplacer des fichiers qui existent déjà sur la destination, vous souhaitez également ajouter-n
ndemou

1
@Ruslan: C'est vrai, mais vous ne pouvez pas vous déplacer sans copie sur les systèmes de fichiers avec aucune méthode. Même mv /fs1/file /fs2/(sur tous les systèmes de fichiers) effectuera une copie puis une suppression.
palswim

2
Bien, mais mvcela fonctionnera (à condition que le répertoire cible n'existe pas encore), même s'il n'est pas "efficacement" ou quoi que vous l'appeliez, cp -rléchouera.
Ruslan

23

Je recommanderais ces quatre étapes:

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

ou mieux encore, voici un script qui implémente une sémantique semblable à 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

Args are SOURCE, DEST
schuess

Cela semble très utile. Je suis tenté de l'utiliser pour nettoyer mon disque dur. Est-ce que d'autres experts peuvent en parler avant que je confie un tas de sauvegardes à ce script? :-)
LarsH

BTW, si vous vouliez faire l'équivalent de rsync -u(ne mettre à jour que si plus récent), mv(dans certaines versions au moins) peut également prendre l' -uoption. Toutefois, dans ce cas, vous voudrez peut-être supprimer les répertoires sources non vides, ainsi que ceux vides, pour couvrir les cas où les fichiers de l’arborescence des sources ne sont pas plus récents. @schuess: Il semble y avoir plusieurs arguments SOURCE, si vous en avez besoin.
LarsH

1
Cela ne gère pas bien les espaces. J'ai essayé avec certains répertoires contenant des espaces et j'ai fini avec une série infinie de répertoires imbriqués.
dimanche

16

Voici un moyen de fusionner les répertoires. Il est beaucoup plus rapide que rsync car il se contente de renommer les fichiers au lieu de les copier puis de les supprimer.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'

C'est intéressant, mais seulement vaguement pertinent pour le sujet et pas même à distance ce que l'utilisateur a demandé.
Shadur

17
En réalité, le code de Jewel fait précisément ce que l'utilisateur a demandé, à l'exception de la création de répertoires manquants. Peut-être devriez-vous regarder à nouveau?
Jonathan Mayer

3
J'ajouterais pour utiliser "-print0" dans find et "-0" dans xargs car il y a des fichiers qui ont des espaces dans les noms. En outre, il existe un petit problème: si un nom contient des parenthèses, il ne sera pas déplacé.
markuz

2
Ceci est beaucoup plus rapide que rsync pour un petit nombre de fichiers, mais il crée un nouveau processus pour chaque fichier. Les performances sont donc catastrophiques avec un grand nombre de petits fichiers. La réponse de @ palswim ne souffre pas de ce problème.
b0fh

2
La commande échouera si, dans destest déjà un répertoire portant le même nom que dans source. Et les fichiers seront déplacés vers un dest, qui est dans source. La commande ne fait rien d'autre quemv source/* source/dest/.
ceving

3

Une façon d'y parvenir serait d'utiliser:

mv folder/* directory/folder/
rmdir folder

Tant qu'il n'y a pas deux fichiers portant le même nom en folderet directory/folder, vous obtiendrez le même résultat, à savoir la fusion.


3
Comment fonctionne exactement rm folder?
JakeGould

5
@JakeGould Pas du tout. :)
2014 à 6h58

rm folder -fRmarche toujours pour moi
Octopus

2
Sachez que cela ne fonctionnera pas pour les fichiers cachés
b0fh

2

Pour les copies les plus pures, j'utilise la méthode de copie en lecture de blocs tar (-) B.

exemple, depuis le chemin source ('cd' là-bas si nécessaire):

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

cela crée une copie exacte de l'arborescence source, avec le propriétaire et les autorisations intactes. Et si le dossier cible existe, les données seront fusionnées. Seuls les fichiers déjà existants seront écrasés.

Exemple:

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

Lorsque l'action de copie réussit, vous pouvez supprimer la source ( rm -rf <source>). Bien sûr, ce n’est pas un geste exact: les données seront copiées jusqu’à ce que vous supprimiez la source.

En option, vous pouvez être prolixe (afficher à l'écran le fichier en cours de copie), avec -v: tar cBvf -

  • c: créer
  • B: lire le bloc complet (pour la lecture du tuyau)
  • v: verbeux
  • f: fichier à écrire
  • x: extrait
  • -: stdout / stdin

sourcefolderpeut aussi être *(pour tout dans le dossier en cours)


Spécifier f -tar est généralement inutile - la valeur par défaut est de lire stdin / write sur stdout.
Muru

1

Voici un script qui a fonctionné pour moi. Je préfère mv à rsync, alors j'utilise les solutions de Jewel et 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

Cette solution n'échappe pas correctement les noms de chemins, soyez prudent.
user12439

@ user12439, je mettrai à jour la solution si vous m'indiquez quelle partie corriger.
xer0x

1

Ce n'est pas une bonne idée d'utiliser des commandes telles que cp ou rsync. Pour les gros fichiers, cela prendra beaucoup de temps. mv est beaucoup plus rapide puisqu'il ne fait que mettre à jour les inodes sans copier physiquement les fichiers. Une meilleure option consiste à utiliser le gestionnaire de fichiers de votre système d'exploitation. Pour Opensuse, il existe un gestionnaire de fichiers appelé Konquerer. Il peut déplacer des fichiers sans les copier. Il a "couper et coller" comme dans Windows. Il suffit de sélectionner tous les sous-répertoires du répertoire A. Cliquez avec le bouton droit de la souris et placez-vous dans le répertoire B qui peut contenir des sous-répertoires du même nom. Cela va les fusionner. Il existe également des options pour écraser ou renommer des fichiers portant le même nom.


1
OP demande ce qui se passe quand mvest utilisé.
don_crissti

0

Solution python

Comme je ne pouvais pas trouver une solution préexistante satisfaisante, j'ai décidé de créer un script Python rapide pour y parvenir.

En particulier, cette méthode est efficace car elle ne parcourt l'arborescence du fichier source qu'une fois ascendante.

Cela vous permettra également de modifier rapidement des options telles que la gestion de l'écrasement de fichiers à votre guise.

Usage:

move-merge-dirs src/ dest/

déplacera tout le contenu de src/*dans dest/et src/disparaîtra.

move-merge-dirs

#!/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 en amont .

Voir aussi: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

Cette commande permet de déplacer des fichiers et des dossiers vers une autre destination:

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

Rappelez - vous : la mvcommande ne fonctionnera pas si le dossier est bei̲n̲g me̲r̲ge̲d̲ (c’est-à-dire qu’un autre dossier portant le même nom existe déjà dans la destination) et que le dossier est dactylographié .

mv: impossible de déplacer "/ source / chemin / dossier" vers "/ cible / destination / dossier": répertoire non vide

Si le dossier de destination est vide, la commande ci-dessus fonctionnera correctement.

Donc, afin de fusionner les deux dossiers dans tous les cas,
soit le faire en 2 commandes:

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

Ou combinez les deux comme une commande unique:

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

mv = move
cp = copy
rm = remove

-r pour le répertoire (dossier)
-f force l'exécution

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.