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 на самом деле все еще требует отдельной операции ввода-вывода).
- это не работает, если источник и назначение находятся на разных файловых системах