Может кто-нибудь объяснить мне такое поведение, которое я заметил недавно, когда я синхронизировал каталог с другим сервером через rsync? Я хотел исключить подкаталог "сделано" в этом случае.

Когда я поместил параметры для использования с rsync в переменную, этот каталог не был исключен. Но это было, когда я поместил параметры прямо за вызовом rsync. Изменения в «-av» имели значение, но исключение не сработало.

rsync 3.0.9-18, на bash, CentOS 7.4

Не исключено:

$ RSYNC_OPTIONS='-av --exclude "done"'
$ touch done/test.ignore && rsync ${RSYNC_OPTIONS} ${SOURCEDIR} ${TARGET}
sending incremental file list
done/test.ignore

sent 132 bytes  received 32 bytes  109.33 bytes/sec
total size is 0  speedup is 0.00

Не входит:

$ touch done/test.ignore && rsync -av --exclude "done" ${SOURCEDIR} ${TARGET}
sending incremental file list

sent 41 bytes  received 12 bytes  106.00 bytes/sec
total size is 0  speedup is 0.00

2 ответа2

2

Я повторил проблему и использовал set -x чтобы увидеть, как на самом деле выглядят обе команды. Оказалось эта команда

rsync ${RSYNC_OPTIONS} ${SOURCEDIR} ${TARGET}

на самом деле эквивалентно этому

rsync -av --exclude '"done"' ${SOURCEDIR} ${TARGET}

Обратите внимание на кавычки в кавычках. Ваш образец не done ; это "done" , как если бы каталог, который вы хотите исключить, содержал двойные кавычки в своем фактическом имени.


Чтобы почти это исправить, вы можете объявить переменную без этих кавычек:

RSYNC_OPTIONS='-av --exclude done'                           # poor fix, don't
rsync ${RSYNC_OPTIONS} "${SOURCEDIR}" "${TARGET}"

Но это будет иметь неприятные последствия, если шаблон содержит пробелы и т.д. Другой подход может быть с eval:

RSYNC_OPTIONS='-av --exclude "name  with  double  spaces"'
eval rsync "${RSYNC_OPTIONS}" '"${SOURCEDIR}" "${TARGET}"'   # not recommended

eval проанализирует строку во второй раз. Это очень трудно использовать правильно и безопасно. Я дважды заключаю в кавычки ${RSYNC_OPTIONS} поэтому "name with double spaces" не теряет двойных пробелов. Я заключил в кавычки "${SOURCEDIR}" "${TARGET}" , поэтому эти переменные не раскрываются сразу (иначе их содержимое подвергнется расширению). Это сложно!

Кроме того, как насчет name "with' quotes? Чтобы получить эту точную строку в качестве аргумента-опции для rsync --exclude вам нужны неясные кавычки и экранирование в объявлении RSYNC_OPTIONS . Есть больше причин, чтобы избежать eval .


Реальным решением является использование массива в Bash. Примечание массивы не являются переносимыми.

RSYNC_OPTIONS=(-av --exclude 'name with  spaces and $u(h')
rsync "${RSYNC_OPTIONS[@]}" "${SOURCEDIR}" "${TARGET}"

Я понимаю, почему вы не ${RSYNC_OPTIONS} в своем первоначальном подходе. Вы должны были процитировать ${SOURCEDIR} и (отдельно) ${TARGET} . Приведенная выше команда правильно указывает каждую переменную.

Или, возможно, ${SOURCEDIR} предназначался для указания нескольких источников? Это было бы причиной не заключать его в кавычки, но тогда это может привести к тем же проблемам, что и ${RSYNC_OPTIONS} . В этом случае вам также следует обязательно использовать переменную массива.

Также учитывайте имена переменных в нижнем регистре.

-1

Как насчет размещения ваших цитат, как это?

RSYNC_OPTIONS='-av --exclude '"done"''

Я проверил это только по упрощенной команде:

SEP='-F ";"'
echo "1;2;3" | awk ${SEP} '{print $2}'

(не работает)

SEP='-F '";"''
echo "1;2;3" | awk ${SEP} '{print $2}'

(за работой)


Редактировать:

С техникой @Kamil, массив работает хорошо:

SEP=(-F ";")
echo "1;2;3" | awk ${SEP[@]} '{print $2}'

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