Конкатенация строк Bash, используемая для построения списка параметров

12

Учитывая этот кусок Баш:

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo $PARMS
rsync ${PARMS} . ${TARGET}

Эхо показывает строку PARMS, как и ожидалось, ошибки не отображаются, но rsync работает тихо, как если бы опции, добавленные + =, не существовали. Тем не менее, это работает как ожидалось:

PARMS='-rvu'
rsync ${PARMS} --delete --exclude='.git' . ${TARGET}

Наверное, я что-то напортачил с кавычками bash (с ними всегда были проблемы), но не совсем уверен, что и почему параметры игнорируются, даже если строка, кажется, построена правильно.

neuviemeporte
источник
1
echo "$PARMS"и rsync "${PARMS}"...
Джейсонвриан
Это работает для меня с bashверсией 4.2.25 без каких-либо изменений.
Энтон

Ответы:

17

Есть разница между:

PARMS+="... --exclude='.git'"

и

... --exclude='.git'

В первом случае одинарные кавычки находятся внутри самих кавычек, поэтому они буквально присутствуют в подстановочном тексте, заданном в rsyncкачестве аргументов. rsyncполучает аргумент, значение которого --exclude='.git'. Во втором случае одинарные кавычки интерпретируются оболочкой во время их написания, потому что они не находятся внутри самих кавычек и их rsyncможно увидеть --exclude=.git.

В этом случае вам не нужны одинарные кавычки - .gitэто совершенно корректное слово оболочки само по себе, без специальных символов, поэтому вы можете использовать его буквально в команде.

Лучше всего для такого рода вещей использовать массив :

PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"

Это создает вашу команду как отдельные слова, с любой цитатой, которую вы хотите интерпретировать во время написания строки массива. "${PARMS[@]}"расширяется до каждой записи в массиве как отдельный аргумент, даже если в самом аргументе есть специальные символы или пробелы, поэтому он rsyncвидит то, что вы написали, как вы это имели в виду.

Майкл Гомер
источник
bashвыполнено разделение слов после ${PARMS}расширения. Таким образом, единственная цитата также была интерпретирована оболочкой.
Cuonglm
2
Попытайся! Я сделал. Кавычки остаются, и если между ними есть пробелы, они все равно являются точками разделения.
Майкл Гомер
@Gnouc: На странице Баш человека: «Цитата удаления: После предыдущих расширений, все котирующиеся вхождения символов \ , 'и ". , Что не является результатом одной из вышеуказанных расширений удаляются» «вышеупомянутые расширения» включает в себя расширение параметров, которое выполняет расширение ${PARMS}.
Cam
Благодарю. Итак, я понимаю, что в этом случае пропуск одинарных кавычек внутри двойных будет работать, но ради полноты - что, если мне нужно было заключить в кавычки некоторые специальные символы и я не хотел использовать ваш последний подход?
neuviemeporte
Если ваши специальные символы не являются частью IFS(как правило, пробела), вам не нужно их заключать в кавычки. Если это так, то вам не повезло, если вы не взломали что-то вместе с ним eval- это, в общем-то, небольшая ошибка, и массивы - правильный способ справиться с этим.
Майкл Гомер
2

В дополнение к ответу @Michael Homer вы можете использовать bash функцию eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
cuonglm
источник
2
Вы можете, но не должны. Массивы были добавлены специально, чтобы избежать такого использования eval.
chepner