5

Я только начал использовать 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 ответа3

6

Сообщение об ошибке говорит вам, что он не может 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.

2

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

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

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

Заметки:

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

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

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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .