1

Я создаю скрипт для копирования / контрольной суммы файлов .... под управлением последней Mac OS X / FreeBSD с возможностью портирования в CentOS, Debian или OpenBSD

Подробнее о скрипте:

  1. проверить, содержит ли исходный путь файлы / подкаталоги
  2. создать контрольную сумму файла (ов) для каждого dir / subdir
  3. tar / сжатие в целевой путь
  4. проверить целостность файла в целевом пути

Конечно, это параноидально, потому что проверки целостности файлов выполняются на уровне HW/HDD и могут быть легко проверены SMART, но спустя десять лет я не могу проверить целостность оригинала. Контрольная сумма создается на карте CF/XD и является оригинальной ... вы можете копировать столько, сколько хотите, и никогда не беспокоиться о так называемых гнилых битах, ошибках HW и так далее.

Конечно, можно использовать и rsync, но мне не нравится идея устаревших контрольных сумм MD5/SHA1 с возможными коллизиями. Чтобы оказаться в нужном месте в нужное время и сделать одно уникальное фото, нужны часы работы, "удачи" и пота. Если вы потеряете оригинальный RAW… он исчезнет навсегда, останется только память.

"Выживают только параноики" - Энди Грув

У меня есть простой рабочий скрипт для шага 1. в скрипте:

today=`date +%Y-%m-%d`
CHK='shasum -a512'
CHK_OUTPUT=($today)-checksum.txt
find . -type f ! -name  ".*" -maxdepth 1 -exec $CHK {} \; > "$CHK_OUTPUT"

Я получаю файл контрольной суммы, как и ожидалось, но вопрос «Можем ли мы сделать его лучше?"

...cf83e1357eef47417a81a538327af927da3e  ./(2017-07-19)-checksum.txt

Я хочу избавиться от надоедливых./ Поэтому я закодировал следующее ...

find ./ -type f ! -name  ".*" -maxdepth 1 -exec bash -c '$CHK $(basename {}) > $CHK_OUTPUT' \;

к сожалению, я получаю следующую ошибку

bash: ${CHK_OUTPUT}: ambiguous redirect

еще одна попытка

    find ./ -type f ! -name  ".*" -maxdepth 1 -exec bash -c '$CHK $(basename {})' \; > $CHK_OUTPUT

это как-то работает, но со странными результатами

Я потерпел неудачу в UTFM и RTFM, и я понятия не имею, как даже спросить Google :-D

Кто-нибудь может подсказать, как это сделать, пожалуйста?

С уважением

Дэвид

2 ответа2

2

Как передать аргументы в подоболочку в -exec

С помощью find 's -exec вы можете передавать сложные команды с помощью подоболочки, как вы правильно определили. Тем не менее, есть некоторые проблемы с вашим подходом.

Ваша внешняя переменная $CHK не будет расширена, поскольку она находится в одинарных кавычках. То, что вы можете сделать, чтобы subshell знал эту переменную, это export ее раньше:

$ foo=bar
$ find . -type f -exec sh -c 'echo "$foo"' \;
(returns an empty line for every file found)

$ export foo=bar
$ find . -type f -exec sh -c 'echo "$foo"' \;
(returns "bar" for every file found)

Экспорт переменной делает ее частью вашей среды, которую могут читать подоболочки. Или вы передаете его в качестве отдельного аргумента в подоболочку, которая здесь описана ниже:

$ foo=bar
$ find . -type f -exec sh -c 'echo "$0"' "$foo" \;
(returns "bar" for every file found)

Конечно, вы можете использовать $1 , $2 т.д. И использовать {} в качестве аргумента для вашей подоболочки, чтобы использовать фактическое имя файла. И не забудьте процитировать ваши переменные.

Ваш конкретный случай

Вы можете просто переписать вашу команду в:

shasum -a512 * > "$CHK_OUTPUT"

потому что shasum достаточно умен, чтобы выполнять работу в одной команде, читая несколько файлов, без цикла или find . По умолчанию * не включает файлы, начинающиеся с точки (но вы можете изменить это с помощью shopt -s dotglob), поэтому параметры find не нужны, особенно если maxdepth равен 1.

Но давайте представим, что shasum не был таким умным, поэтому я могу дать вам еще несколько вариантов. Если вы хотите использовать find , вот как вы будете обрабатывать несколько аргументов:

CHK='shasum -a512'
find ./ -type f ! -name  ".*" -maxdepth 1 -exec \
sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > "$CHK_OUTPUT"

Но даже тогда все это можно переписать как более читабельное:

today=$(date +%Y-%m-%d)
for f in *; do shasum -a512 "$f" > "($today)-checksum.txt"; done

Зачастую обходить файлы легче, чем использовать find , хотя существует ограничение на количество файлов, которые можно обработать таким образом, из-за расширения * - в какой-то момент это будет слишком долго для вашей командной строки (конкретный предел зависит на вашей ОС и оболочке).

И вы, конечно, могли бы сделать это рекурсивно с shopt -s globstar в Bash ≥ 4.0:

shopt -s globstar
for f in **/*; do …; done

Но это опять же, как:

shasum -a512 **/* > "$CHK_OUTPUT"
0

ОБНОВИТЬ

К сожалению, нижеупомянутая команда все еще создает пустой контрольный файл .txt в подкаталогах, где нет файлов.

find ./ -type f \( ! -iname ".*" ! -iname "\(????-??-??\)-checksum.txt" \) -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > $CHK_OUTPUT

Я не знаю почему, но есть клудж

# remove empty checksum.txt
    if [[ -f $CHK_OUTPUT ]] && [[ ! -s $CHK_OUTPUT ]]; then
        rm $CHK_OUTPUT
    fi

================================================== ===

окончательное решение ... кажется, работает до сих пор **

find ./ -type f \( ! -iname ".*" ! -iname "\(????-??-??\)-checksum.txt" \) -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > $CHK_OUTPUT

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