Як перезаписати цільові файли з mv?


154

У мене є тонна файлів і файлів у підкаталозі, який я хочу перенести до батьківського каталогу. У цільовому каталозі вже є деякі файли та файли, які потрібно перезаписати. Файли, які присутні лише в цілі, слід залишати недоторканими. Чи можу я змусити mvце зробити? Він ( mv * ..) скаржиться

mv: cannot move `xyz' to `../xyz': Directory not empty

Що я пропускаю?


3
Ви пробували mv -f?
sakisk

Цікаво, чому mv -fневірна відповідь.
Педро Лобіто

@PedroLobito: Тому що це не працює? -f лише перезаписує файли, але не працює, якщо ви переміщуєте підкаталоги, які існують у пункті призначення та не порожні.
EricSchaefer

Відповіді:


117

Вам доведеться скопіювати їх до місця призначення, а потім видалити джерело, використовуючи команди, cp -r * ..за якими слідують rm -rf *.

Я не думаю, що ви можете "об'єднати" каталоги за допомогою mv.


4
Ну, це я не хотів робити, бо це займе багато часу ... Спасибі все одно.
EricSchaefer

12
Імовірно, mvце швидше, оскільки ви перебуваєте в одній файловій системі? Що робити, якщо ви використовуєте cp -lдля створення жорстких посилань, а не для фактичного переміщення файлів?
mattdm

6
Ви повинні використовувати cp -aзамість cp -r, щоб зберегти атрибути файлів (часова мітка, дозволи тощо).
dotancohen

2
Для людей, які прибувають сюди пізно через Google, відповідь нижче @palswim імітує поведінку mv, створюючи нові жорсткі посилання на дані, а потім видаляючи старі посилання. Коротка відповідь cp -rl source destination && rm -r source.
Вільям Еверетт

72

rsyncМожливо, тут буде кращий варіант. Це так само просто rsync -a subdir/ ./.

Мій тест дерево filename: contentsФормат:

./file1:root
./file2:root
./dir/file3:dir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Запуск rsync:

$ rsync -a -v subdir/ ./
sending incremental file list
./
file1
dir/
dir/file3

Дає:

./file1:subdir
./file2:root
./dir/file3:subdir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

А потім, щоб наслідувати mv, ви, ймовірно, хочете видалити вихідний каталог:

$ rm -r subdir/

Давання:

./file1:subdir
./file2:parent
./dir/file3:subdir
./dir/file4:dir

Якщо це неправильно, чи можете ви надати подібний приклад (наприклад, використовуючи моє тестове дерево вгорі цієї відповіді) з бажаним результатом?


1
копії rsync. Це питання стосується переїзду.
Жиль

1
@Gilles: Дякую Я додав rm -rнаприкінці, щоб зробити його в основному таким же, як mv.
Мікель

3
copy-then-delete не є еквівалентом mv, коли джерело та призначення знаходяться в одній файловій системі. mvє атомним, зберігає номери inode (тому файл може залишатися відкритим) і не потребує часу та місця для копіювання.
Жиль

5
@Gilles: Я усвідомлюю це, але на даний момент провідна відповідь cp -r; rm -r. Я думаю, що в цьому сенсі rsyncтакож варто згадати.
Мікель

Я вже робив це з cp / rm (це було терміново). Це дійсно зайняло багато часу. Жил сценарій, певно, був би набагато швидшим, але він теж занадто пізно.
EricSchaefer

47

rsyncможе видалити джерело після копіювання з --remove-source-filesпараметром. Це має бути зручним способом робити те, що ви хочете.

З rsync man page:

        --remove-source-files   sender removes synchronized files (non-dir)

Це справді найкраща відповідь. Я не міг використати cp -r; rmчерез брак вільного місця. Натомість rsync --remove-source-filesобидва мінімізованого використовуваного дискового простору уникали копіювання над тими самими файлами.
гуака

20

Ви можете зробити це з cpі rm, але без копіювання величезної кількості даних , які ви (імовірно) намагаюся уникнути передач. @mattdm наголосив на цьому у своєму коментарі , а відповідь на інше питання має більш повне обговорення різних варіантів.

cp -rlf source destination
rm -r source

По суті, -lопція cpкоманди створює жорсткі посилання на файли, а не копіювання їх даних у нові файли.


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

Це справді відповідь, яка їм була потрібна. Я тільки що зробив це з 60 ГБ тисяч маленьких файлів електронної пошти з кіровими, і це зайняло всього 21 секунду.
лабрадорт

Це також маршрут, який я взяв - я просто змінив його, cp -al source destinationщоб зберегти інформацію та дозволи власника.
piit79

Я думаю, щоб насправді зробити те, що просив ОП, ви повинні додати опцію -f, інакше вона не перезапишеться, якщо файл існує. Чи можете ви підтвердити це чи я роблю щось не так?
дас Кекс

@dasKeks: Насправді, cpза замовчуванням перезаписується. -fОпція намагається видалити файл (як в rm) , перш ніж намагатися скопіювати заново, що може допомогти , якщо процес не може відкрити файл для запису, хоча деякі люди вважали за краще б бачити , що сталася помилка , замість цього. Я не знаю, чи це стосується ОП, але я все-таки додав -fпрапор до своєї відповіді.
palswim

8

Ось сценарій, який переміщує файли з-під /path/to/source/rootу відповідний шлях під /path/to/destination/root.

  • Якщо каталог існує як у вихідному, так і в цільовому адресах, вміст переміщується та об'єднується рекурсивно.
  • Якщо файл або каталог існує в джерелі, але не в цільовому пункті призначення, він переміщується.
  • Будь-який файл або каталог, які вже є в пункті призначення, залишаються позаду. (Зокрема, об’єднані каталоги залишаються позаду у джерелі. Це нелегко виправити.)

Обережно, неперевірений код.

export dest='/path/to/destination/root'
cd /path/to/source/root
find . -type d \( -exec sh -c '[ -d "$dest/$0" ]' \; -o \
                  -exec sh -c 'mv "$0" "$dest/$0"' {} \; -prune \) \
    -o -exec sh -c '
        if ! [ -e "$dest/$0" ]; then
          mv -f "$0" "$dest/$0"
        fi
' {} \;

Два виправлення: вам потрібен знак " \;перед" -oу першому рядку findкоманди, і вам не слід бігти !в if- це просто !, ні\!
llhuii

2

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

cd xyz
tar -cvzpf tmp.tar.gz *
mv tmp.tar.gz ../tmp.tar.gz
cd ..
tar -xvzpf tmp.tar.gz
rm -rf xyz
rm -f tmp.tar.gz

Це працює лише в тому випадку, якщо у вас є додатковий простір для стисненого файлу tar. А ви дійсно хочете зберігати тимчасовий файл смоли після вилучення?
Антон

Відредагований код для видалення tmp-файлу. Сьогодні у більшості випадків простір не є проблемою. #terabyteages
Саймон Краус

0

Якщо у вас достатньо місця для зберігання, ви можете зробити це наступним чином:

mv -bfv directory_1/* directory_2/ # all duplicate source files/directories 
                                   # will have ~ appended to them
find -name "*~" -delete            # will recursively find and delete all files 
                                   # with ~ on the end

Переконайтеся, що немає важливих файлів із знаком ~ в кінці, але якщо вони є, ви можете додати --suffix=whateveryouwantзамість типового.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.