BASH - объединить каталоги при использовании mv

Я только начал использовать BASH, поэтому я могу переместить все содержимое одного каталога в другой. Но проблема в том, что я ожидал слияния каталогов. Я использую задание cron для запуска этого скрипта.

#!/bin/bash

shopt -s dotglob nullglob
mv mv_schedule/* public_html/

Источник: /mv_schedule/ (содержит папки / файлы)

/files/
4.html
5.html
/assets/
sitemap.xml

Место назначения: /public_html/ (предыдущие папки уже существуют)

/files/
1.html
2.html
3.html
/assets/
sitemap.xml

Так что я хочу, это для всех новых файлов из этих каталогов внутри /mv_schedule/ слиться с теми в /public_html/и для файлов, которые уже существуют там, должны быть заменены /mv_schedule/,

В настоящее время он возвращает эту ошибку по электронной почте, когда я пробую это с текущим сценарием:

mv: cannot move `mv_schedule/assets' to `public_html/assets': Directory not empty

Как я могу это исправить?

3 ответа

Решение

Сообщение об ошибке говорит вам, что он не может mv каталог ресурсов, потому что в нем есть файлы. Под каталогом подразумевается создание копии, а затем ее удаление, и она не может стереть ее, поскольку она все еще содержит некоторые файлы. Это означает, что вы должны также просматривать файлы внутри ресурса и даже подкаталоги и файлы в подкаталогах внутри ресурса, если они существуют. Но выданная команда mv может копировать (и стирать) только то, что присутствует в каталоге mv_schedule, но не то, что присутствует в подкаталогах (например, asset) в mv_schedule.

Вам нужна команда, которая спускает дерево каталогов до последнего листа и копирует его, что-то вроде параметра -r или -R в rm, chmod, chown. Однако у mv такой опции нет, поэтому вам придется использовать команду find, которая спускается по всему дереву каталогов из указанного вами корня, а затем выполняет заданное вами действие. В вашем случае подходящая команда:

SOURCE_DIR=$1
TARGET_DIR=$2
find $SOURCE_DIR -name '*' -type f -exec mv -f {} $TARGET_DIR \;

Это соберет все файлы в один целевой каталог. Предполагается, что вы передали исходный и целевой каталог в качестве параметров строки ввода в скрипт bash. Опция -f предотвращает запрос подтверждения в случае перезаписи, вы можете изменить это на -n (не перезаписывать) или -i (спрашивать перед перезаписью).

Если вместо этого вы хотите сохранить структуру каталогов, помните, что команда cp имеет возможность спускаться по дереву каталогов, так что вы можете использовать это, а затем rm, поскольку эта команда также имеет возможность спускаться по деревьям. Один из возможных наборов команд:

SOURCE_DIR=$1
TARGET_DIR=$2
cp -a $SOURCE_DIR $TARGET_DIR
rm -rf $SOURCE_DIR

Обратите внимание на опцию -a в cp: она сохраняет временные метки и владельца. Если вас это не волнует, вместо этого вы можете использовать -R.

Существует более общее обсуждение этой проблемы в разделе Unix.

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

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

Заметки:

  • Вы также можете использовать -P (--no-dereference - не отменять ссылки на символические ссылки) или -a (--archive - сохранить все метаданные, включая -P вариант), в зависимости от ваших потребностей.
  • Несмотря на то, что используются два этапа "ввода-вывода", эти этапы представляют собой относительно простые операции с метаданными, включающие нулевую передачу "данных". Таким образом, этот метод на величины быстрее, чем cp (без -l) или решение на основе rsync.
  • Это не работает, если ваши исходная и целевая папки находятся в разных файловых системах

Отличный ответ от Palswim. (Кроме того, спасибо за голосование за те два последних пункта, которые необходимы, чтобы иметь возможность поднять голос сам!)

Cp -rl; rm решает проблему с минимальным вводом / выводом (cp и rsync тупо дороги в этом отношении).

Есть две незначительные проблемы с этим:

  • он не на 100% атомарен - он требует двух операций ввода-вывода (это относится и к методу rsync, так как --remove-source-files на самом деле все еще требует отдельной операции ввода-вывода).
  • это не работает, если источник и назначение находятся на разных файловых системах
Другие вопросы по тегам