Слияние папок с mv?


149

Если я использую mvдля перемещения папку с именем «папка» в каталог, который уже содержит «папка», они будут объединены или она будет заменена?

Ответы:


120

mvне может объединить или перезаписать каталоги, произойдет сбой с сообщением «mv: не может переместить« a »в« b »: каталог не пустой» , даже если вы используете эту --forceопцию.


Вы можете обойти это, используя другие инструменты (например rsync, findили даже cp), но вам нужно тщательно рассмотреть последствия:

  • rsyncможет объединить содержимое одного каталога в другой (в идеале с опцией --remove-source-files1 можно безопасно удалить только те исходные файлы, которые были успешно переданы, и с обычным вариантом разрешения / владения / сохранения времени, -aесли вы хотите)
    но это операция полного копирования и, следовательно, может быть очень интенсивным диском.
  • В настоящее время предпочтительный варианта: Вы можете комбинировать rsync«s --link-dest=DIRварианта (чтобы создать жесткие ссылки вместо копирования содержимого файла, где это возможно) и , --remove-source-filesчтобы получить семантическую очень похожи на обычный mv.
    Для этого --link-destдолжен быть указан абсолютный путь к исходному каталогу (или относительный путь от места назначения к источнику ).
    ... но это используется --link-destнепреднамеренным образом (что может или не может вызывать осложнения), требует знания (или определения) абсолютного пути к источнику (в качестве аргумента --link-dest) и снова оставляет пустую структуру каталогов для очистки как за 1 .
  • Вы можете использовать,find чтобы последовательно воссоздать исходную структуру каталогов на цели, затем индивидуально переместить фактические файлы
    но это должно повторяться через источник несколько раз и может встретиться с условиями гонки (новые каталоги создаются в источнике во время многоэтапного процесса )
  • cpможет создавать жесткие ссылки (проще говоря, дополнительные указатели на тот же существующий файл), что создает результат, очень похожий на слияние mv(и очень эффективный с точки зрения ввода-вывода, так как создаются только указатели и никакие фактические данные не должны копироваться)
    но это снова страдает от возможного состояния гонки (новые файлы в источнике удаляются, даже если они не были скопированы на предыдущем шаге)

Какой из этих обходных путей (если таковой имеется) является подходящим, во многом зависит от вашего конкретного варианта использования.
Как всегда, подумайте, прежде чем выполнять какую-либо из этих команд, и сделайте резервные копии.


1: Обратите внимание, что rsync --remove-source-filesникакие каталоги не будут удалены, поэтому вам нужно будет сделать что-то подобное find -depth -type d -empty -deleteпотом, чтобы избавиться от пустого дерева каталогов исходного кода.


Похоже, вы пробовали только одну реализацию mv. Этот ответ был бы лучше с более широкой правдой. Linux, BSD и «настоящий» Unix, или ссылка от POSIX или SUS.
Уоррен Янг

@WarrenYoung Вы правы, я только попробовал mvреализацию используемого Debian - акцент быть на попробовал , так как страница руководство не упоминает это поведение ...
n.st

38
Недостатком rsync является то, что он на самом деле копирует данные, а не просто изменяет жесткую ссылку, которая потенциально ресурсоемка, если вы имеете дело с большим количеством данных.
Джонатан Майер

7
@Keith Обратите внимание, что --deleteудаляются только файлы в целевом каталоге, которые не существуют в исходном каталоге.
16:18

1
@JonathanMayer rsync как несколько функций, связанных с жесткой связью. Например, вы можете просто сохранить жесткие ссылки с помощью -Hфункции или вы можете жестко связать файлы в месте назначения, используя --link-dest. Посмотрите справочную страницу, прежде чем их использовать.
Алло

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

Будет ли это удалить исходные файлы, как в комментарии от n.st?
Доминик

3
Нет, я бы предпочел сделать это в два этапа по соображениям безопасности. Объединенный и удаленный источник необратим. Шаг добавления в n.st anwer также необходим (для удаления каталогов).
Фази

3
--remove-source-filesимеет преимущество удаления только тех файлов, которые были успешно переданы, так что вы можете использовать их findдля удаления пустых каталогов, и у вас останется все, что не было передано без проверки rsyncвывода s.
n.st

4
Но на самом деле он не движется - влияние на скорость огромно, если задействованы большие файлы.
Алекс

Но вы не можете совершать чистые движения и слияния на самом деле.
Фази

64

Вы можете использовать -lопцию команды cp , которая создает жесткие ссылки на файлы в одной файловой системе вместо полных копий данных. Следующая команда копирует папку source/folderв родительскую папку ( destination), которая уже содержит каталог с именем folder.

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

Вы также можете использовать -P( --no-dereference- не отменять ссылки на символические ссылки) или -a( --archive- сохранить все метаданные, включая -Pопцию), в зависимости от ваших потребностей.


7
@rautamiekka: Я полагаю, вы спрашиваете причину использования жестких ссылок. Если вы не знаете, что такое жесткие ссылки и почему вы должны их использовать, то вам, вероятно, не следует идти по этому пути. Однако создание жестких ссылок не дает полной копии, поэтому эта операция займет на порядки меньше времени, чем полная копия. И вы будете использовать жесткие ссылки, а не мягкие ссылки, чтобы вы могли удалять исходные файлы и по-прежнему иметь правильные данные вместо указателей на неверные пути. И cpвместо того , rsyncтак как каждая система имеет cpи каждый имеет знакомство с ним.
Palswim

7
Блеск этого решения может быть просмотрен, чтобы не быть принятым ответом. Это элегантное решение. Вы получаете способность слияния cpсо временем операции mv.
TheHerk

2
если вы знаете, что вам не нужно перемещать файлы, которые уже существуют в месте назначения, вы также хотите добавить-n
ndemou

1
@Ruslan: Это правда, но вы не можете перемещаться без файловой системы без какого-либо метода. Даже mv /fs1/file /fs2/(через файловые системы) выполнит копирование, а затем удаление.
Palswim

2
Правильно, но while mvбудет работать (при условии, что целевой каталог еще не существует), даже если не "эффективно" или как вы его называете, cp -rlпотерпит неудачу.
Руслан

23

Я бы порекомендовал эти четыре шага:

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

или еще лучше, вот скрипт, который реализует семантику, похожую на 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

Арги ИСТОЧНИК,
ДЕСТ

Это выглядит довольно полезным. Я испытываю желание использовать это для очистки моего жесткого диска. Могут ли другие эксперты прокомментировать это, прежде чем я поручаю несколько резервных копий этого сценария? :-)
LarsH

Кстати, если вы хотели сделать эквивалент rsync -u(только обновление, если новее), mv(по крайней мере , в некоторых версиях) также можете воспользоваться этой -uопцией. Однако в этом случае вы можете удалить непустые исходные каталоги, а также пустые каталоги, чтобы охватить случаи, когда файлы в исходном дереве не новее. @schuess: похоже, что может быть несколько аргументов SOURCE, если вам это нужно.
LarsH

1
Это плохо обрабатывает пробелы. Я попробовал несколько каталогов с пробелами в них и получил бесконечную серию вложенных каталогов.
rofer

16

Вот способ слияния каталогов. Это намного быстрее, чем rsync, поскольку он просто переименовывает файлы, а не копирует их, а затем удаляет их.

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

Это интересно, но только в некоторой степени относится к теме, а не даже удаленно, о чем спрашивал пользователь.
Шадур

17
На самом деле код Jewel делает именно то, что просил пользователь, за исключением создания отсутствующих каталогов. Может быть, вы должны посмотреть снова?
Джонатан Майер

3
Я бы добавил, чтобы использовать «-print0» в find и «-0» в xargs, потому что в именах файлов есть пробелы. Кроме того, существует небольшая проблема: если имя содержит круглые скобки, они не будут перемещены.
Маркуз

2
Это намного быстрее, чем rsync для небольшого числа файлов, но он запускает новый процесс для каждого файла, таким образом, производительность с большим количеством маленьких файлов ужасна. Ответ @ palswim не страдает от этой проблемы.
b0fh

2
Команда не будет выполнена, если in destуже является каталогом с тем же именем, что и в source. И файлы будут перемещены в папку dest, которая находится в source. Команда не делает ничего, кроме какmv source/* source/dest/.
ceving

3

Один из способов сделать это - использовать:

mv folder/* directory/folder/
rmdir folder

Пока нет двух файлов с одинаковыми именами в folderи directory/folder, вы получите тот же результат, то есть слияние.


3
Как именно rm folderработает?
JakeGould

5
@ JakeGould Совсем нет. :)
n.st

rm folder -fRу меня всегда работает
Осьминог

2
Имейте в
виду,

2

Для самых чистых копий я использую метод блок-копии tar (-) B.

Например, из исходного пути (там, где это необходимо, 'cd'):

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

это создает точную копию исходного дерева, с владельцем и разрешениями нетронутыми. И если целевая папка существует, данные будут объединены. Только файлы, которые уже существуют, будут перезаписаны.

Пример:

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

Когда действие копирования выполнено успешно, вы можете удалить источник ( rm -rf <source>). Конечно, это не точный ход: данные будут копироваться до тех пор, пока вы не удалите источник.

В качестве опции вы можете быть многословным (отобразить на экране копируемый файл), используя -v: tar cBvf -

  • c: Создайте
  • B: прочитать полный блок (для чтения канала)
  • v: подробный
  • f: файл для записи
  • x: извлечение
  • -: stdout / stdin

sourcefolderтакже может быть *(для чего-либо в текущей папке)


Указывать f -tar обычно не нужно - по умолчанию это чтение из stdin / write в stdout.
Муру

1

Вот сценарий, который работал для меня. Я предпочитаю mv, чем rsync, поэтому я использую решения Jewel и 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

Это решение не правильно экранирует пути, будьте осторожны.
user12439

@ user12439, я обновлю решение, если ты покажешь мне, какую часть исправить.
xer0x

1

Не рекомендуется использовать такие команды, как cp или rsync. Для больших файлов это займет много времени. mv намного быстрее, так как он только обновляет inode без физического копирования файлов. Лучшим вариантом является использование файлового менеджера вашей операционной системы. Для Opensuse существует файловый менеджер Konquerer. Он может перемещать файлы без их фактического копирования. Он имеет функцию «вырезать и вставить», как в Windows. Просто выберите все подкаталоги в каталоге A. Щелкните правой кнопкой мыши и перейдите в каталог B, который может содержать подкаталоги с одинаковыми именами. Это объединит их. Есть также варианты, хотите ли вы перезаписать или переименовать файлы с тем же именем.


1
ОП спрашивает, что происходит, когда mvиспользуется.
don_crissti

0

Решение Python

Так как я не мог найти удовлетворительное уже существующее решение, я решил сделать быстрый скрипт на Python для его достижения.

В частности, этот метод эффективен, поскольку он перемещается по дереву исходного файла только один раз снизу вверх.

Это также позволит вам быстро настроить такие вещи, как обработка перезаписи файлов по своему вкусу.

Использование:

move-merge-dirs src/ dest/

переместит все содержимое src/*в dest/и src/исчезнет.

въезд слияние-каталоги

#!/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 вверх по течению .

Смотрите также: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

Это команда для перемещения файлов и папок в другое место назначения:

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

Помните : mvкоманда не будет работать, если папка b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (то есть другая папка с таким же именем уже существует в месте назначения) и d )e ands̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: невозможно переместить «/ source / path / folder» в «/ target / destination / folder»: каталог не пуст

Если папка назначения пуста, вышеуказанная команда будет работать нормально.

Итак, чтобы объединить обе папки в любом случае,
либо сделайте это в 2 команды:

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

Или объедините оба в одноразовую команду:

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

mv = переместить
cp = копировать
rm = удалить

-r для каталога (папки)
-f принудительное выполнение

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.