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é?
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-files
1 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)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.--link-dest
doit recevoir un chemin absolu vers le répertoire source (ou un chemin relatif depuis la destination vers la source =).--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.find
pour recréer séquentiellement la structure du répertoire source sur la cible, puis déplacer individuellement les fichiers réelscp
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)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.
rsync -av /source/ /destination/
(after checking)
rm -rf /source/
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.
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
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/{}'
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.
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éerB
: lire le bloc complet (pour la lecture de tuyau)v
: verbeuxf
: fichier à écrirex
: extraire-
: stdout/stdinsourcefolder
peut également être *
(pour tout ce qui se trouve dans le dossier actuel)
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
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.
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)
#!/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
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
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.