web-dev-qa-db-fra.com

Fusionner des dossiers avec mv?

Si j'utilise mv pour déplacer un dossier appelé "dossier" vers un répertoire qui contient déjà "dossier" fusionnera-t-il ou sera-t-il remplacé?

157
Dominique

mv ne peut pas fusionner ou écraser les répertoires, il échouera avec le message "mv: ne peut pas déplacer 'a' vers 'b': répertoire non vide", même lorsque vous êtes en utilisant le --force option.


Vous pouvez contourner ce problème à l'aide d'autres outils (comme rsync, find, ou même cp), mais vous devez examiner attentivement les implications:

  • rsync peut fusionner le contenu d'un répertoire dans un autre (idéalement avec le --remove-source-files1 option pour supprimer en toute sécurité uniquement les fichiers source qui ont été transférés avec succès, et avec l'option habituelle de permission/propriété/conservation du temps -a si vous le souhaitez)
    mais il s'agit d'une opération de copie complète, et peut donc être très gourmande en disque.
  • Option actuellement préférée: Vous pouvez combiner rsync's --link-dest=DIR option (pour créer des liens physiques au lieu de copier le contenu du fichier, si possible) et --remove-source-files pour obtenir une sémantique très similaire à une mv régulière.
    Pour ça, --link-dest doit recevoir un chemin absolu vers le répertoire source (ou un chemin relatif depuis la destination vers la source =).
    mais ceci utilise --link-dest d'une manière non intentionnelle (pouvant ou non entraîner des complications), nécessite de connaître (ou de déterminer) le chemin absolu vers la source (comme argument pour --link-dest), et laisse à nouveau une structure de répertoire vide à nettoyer selon 1.
  • Vous pouvez utiliser find pour recréer séquentiellement la structure du répertoire source sur la cible, puis déplacer individuellement les fichiers réels
    mais cela doit se répéter plusieurs fois dans la source et peut rencontrer des conditions de concurrence (de nouveaux répertoires étant créés à la source pendant le multi - processus en plusieurs étapes)
  • cp peut créer des liens durs (simplement mettre des pointeurs supplémentaires vers le même fichier existant), ce qui crée un résultat très similaire à une fusion mv (et est très IO -efficace car seuls des pointeurs sont créés et aucune donnée réelle ne doit être copiée)
    mais cela souffre à nouveau d'une condition de concurrence possible (les nouveaux fichiers à la source sont supprimés même s'ils n'ont pas été copiés dans é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-files ne supprimera aucun répertoire, vous devrez donc faire quelque chose comme find -depth -type d -empty -delete par la suite pour se débarrasser de l'arborescence du répertoire source vide.

127
n.st
rsync -av /source/ /destination/
(after checking)
rm -rf /source/
90
fazie

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

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

Vous pouvez également utiliser le -P (--no-dereference - ne supprime pas les liens symboliques) ou -a (--archive - conserve toutes les métadonnées, comprend également -P option), selon vos besoins.

68
palswim

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 similaire à 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
23
Jonathan Mayer

Voici une façon de fusionner les répertoires. Il est beaucoup plus rapide que rsync car il renomme simplement les fichiers au lieu de les copier puis de les supprimer.

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

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

mv folder/* directory/folder/
rmdir folder

Tant que deux fichiers n'ont pas le même nom dans folder et directory/folder, vous obtiendrez le même résultat, c'est-à-dire la fusion.

4
asheeshr

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

exemple, depuis le chemin source ('cd' là 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 intacts. 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 -)

Une fois la copie réussie, vous pouvez supprimer la source (rm -rf <source>). Bien sûr, ce n'est pas un mouvement exact: les données seront copiées jusqu'à ce que vous supprimiez la source.

En option, vous pouvez être verbeux (afficher à l'écran le fichier copié), avec -v: tar cBvf -

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

sourcefolder peut également être * (pour tout ce qui se trouve dans le dossier actuel)

4
gjs

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

Ce n'est pas une bonne idée d'utiliser des commandes comme cp ou rsync. Pour les fichiers volumineux, cela prendra beaucoup de temps. mv est beaucoup plus rapide car il ne met à jour que 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 une fonction "couper et coller" comme dans Windows. Sélectionnez simplement tous les sous-répertoires du répertoire A. Cliquez avec le bouton droit et "déplacez-vous" sur le répertoire B qui peut contenir des sous-répertoires portant les mêmes noms. Il les fusionnera. Il existe également des options pour remplacer ou renommer des fichiers portant le même nom.

1
simon

Solution Python

Comme je n'ai pas pu trouver de solution préexistante satisfaisante, j'ai décidé de faire un rapide script Python pour y parvenir).

En particulier, cette méthode est efficace car elle ne parcourt l'arborescence des fichiers source qu'une fois de bas en haut.

Il vous permettra également de modifier rapidement des éléments tels que la gestion de l'écrasement de fichiers à votre guise.

Usage:

move-merge-dirs src/ dest/

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

déplacer-fusionner-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

Aucun sens de copier des dossiers vides - YMMV

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd "$SRC";
    # Only one scan - we only need folders with files
    find . -type f | while read FILE ; do
        DIRNAME=`dirname "$FILE"`
        # Create the lowest level directory at once
        if [ ! -d "$DEST/$DIRNAME" ] ; then
            mkdir -v "$DEST/$DIRNAME"
        fi
        mv -v "$FILE" "$DEST/$FILE"
    done
    # Remove the directories no longer needed
    find . -type -d | sort -r | xargs -i rmdir "{}"
    popd
done
  • trouver non exécuté plusieurs fois
  • mkdir -p est exécuté même après avoir trouvé les répertoires séquentiellement

Il s'agit de la commande pour déplacer des fichiers et dossiers vers une autre destination:

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

Rappelez-vous: mv la commande ne fonctionnera pas si le dossier est b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (c'est-à-dire un autre dossier avec le même existe déjà dans la destination) et le d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

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 = se déplacer
cp = copier
rm = supprimer

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

0
Talha Siddiqi

Vous pouvez fusionner a et b avec:

shopt -s dotglob
mv a/* b

Avant mv:

.
├── a
│   ├── c
│   │   └── x
│   └── .x
└── b
    ├── y
    └── .y

Après mv:

.
├── a
└── b
    ├── c
    │   └── x
    ├── .x
    ├── y
    └── .y

dotglob vous permet de déplacer des fichiers de points cachés comme . x

Utilisez rmdir pour supprimer le répertoire vide.

0
sir__finley