Об’єднання папок із 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(і є дуже ефективним IO, оскільки створюються лише покажчики, а фактичні дані не потрібно копіювати)
    ... але це знову страждає від можливої ​​умови перегонів (нові файли в джерелі видаляються, хоча вони не були скопійовані на попередньому кроці)

Який із цих способів вирішення (якщо такий є) підходить, дуже залежатиме від конкретного випадку використання.
Як завжди, подумайте, перш ніж виконати будь-яку з цих команд, і створіть резервні копії.


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


Здається, ви спробували лише одну реалізацію mv. Цю відповідь було б краще з ширшою правдою. Linux, BSD і "справжній" Unix, або посилання від POSIX або SUS.
Warren Young

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

38
Недоліком rsync є те, що він фактично копіює дані, а не просто змінює жорстке посилання, що потенційно вимагає великих ресурсів, якщо ви маєте справу з великою кількістю даних.
Джонатан Майєр

7
@Keith Зауважте, що --deleteвидаляються лише файли в каталозі призначення , які не існують у вихідному каталозі.
n.st

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

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

Чи видалить це вихідні файли, як у коментарі n.st?
Домінік

3
Ні, я вважаю за краще зробити це в два етапи з міркувань безпеки. Об’єднане та вилучене джерело є незворотним. Крок Additon у n.st anwer також потрібен (для видалення каталогів).
фаза

3
--remove-source-filesмає перевагу лише у видаленні файлів, які були успішно передані, тому ви можете використовувати findдля видалення порожніх каталогів, і вам залишиться все, що не було передано, не перевіряючи rsyncвихід.
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
Правильно, але поки 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

Args - ДЖЕРЕЛО, ДЕСТ
schuess

Це виглядає досить корисно. Я спокусився використати це для очищення жорсткого диска. Чи можуть будь-які інші експерти коментувати це, перш ніж я довірити купу резервних копій цього сценарію? :-)
LarsH

BTW, якщо ви хотіли зробити еквівалент rsync -u(лише оновлення, якщо новіші), mv(у деяких версіях принаймні) також можуть скористатися -uможливістю. Однак у цьому випадку ви можете видалити не порожні вихідні каталоги, а також порожні, щоб охопити випадки, коли файли у вихідному дереві не новіші. @schuess: Схоже, може бути декілька аргументів ДЖЕРЕЛА, якщо вам це потрібно.
LarsH

1
Це не добре справляється з пробілами. Я спробував спробувати деякі каталоги з пробілами в них і закінчив нескінченну серію вкладених каталогів.
rofer

16

Ось спосіб, який об’єднає каталоги. Це набагато швидше, ніж rsync, оскільки він просто перейменовує файли, а не копіює їх та видаляє.

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

Це цікаво, але лише невиразно відповідає темі і навіть не віддалено про те, що запитав користувач.
Шадур

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

3
Я хотів би додати використання "-print0" у пошуку та "-0" у xargs, оскільки у іменах є файли, які мають пробіли. Крім того, є невелика проблема, якщо ім'я містить дужки, вони не збираються переміщуватися.
markuz

2
Це набагато швидше, ніж rsync для невеликої кількості файлів, але він видобуває новий процес для кожного файлу, таким чином, продуктивність не відрізняється великою кількістю невеликих файлів. @ palswim відповідь не страждає від цієї проблеми.
b0fh

2
Команда не вдасться, якщо у destвже є каталог з тим самим іменем, що і в source. І файли будуть переміщені в a dest, який знаходиться в source. Команда робить не що інше, якmv source/* source/dest/.
закінчення

3

Одним із способів цього було б скористатися:

mv folder/* directory/folder/
rmdir folder

Поки немає двох файлів не мають таке ж ім'я в folderі directory/folder, ви досягнете того ж результату , тобто злиття.


3
Як саме rm folderпрацює?
JakeGould

5
@JakeGould зовсім не може. :)
n.st

rm folder -fRзавжди працює для мене
Восьминіг

2
Будьте в курсі, що це не буде працювати для прихованих файлів
b0fh

2

Для найчистіших копій я використовую метод копіювання tar (-) B blockread.

Наприклад, з вихідного контуру ('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 / писати в 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

Це рішення не дозволяє належним чином уникати імен шляхів, будьте обережні.
користувач12439

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

1

Недоцільно використовувати такі команди, як cp чи rsync. Для великих файлів знадобиться тривалий час. mv набагато швидше, оскільки він лише оновлює вставки, не копіюючи файли фізично. Кращим варіантом є використання файлового менеджера вашої операційної системи. Для програми «Відкриття» існує файловий менеджер під назвою Konquerer. Він може переміщувати файли, фактично не копіюючи їх. Він має функцію "вирізати та вставити", як у Windows. Просто виберіть усі підкаталоги в каталозі А. Клацніть правою кнопкою миші та перейдіть у каталог В, який може містити підкаталоги з однаковими назвами. Це їх об'єднає. Також є варіанти, чи потрібно перезаписувати або перейменувати файли з тим самим іменем.


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̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: не вдається перемістити '/ source / path / folder' to '/ target / target / folder': Каталог не порожній

Якщо папка призначення порожня, наведена вище команда спрацює нормально.

Отже, щоб об'єднати обидві папки у будь-якому випадку,
виконайте це у двох командах:

$ 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.