Я согласен с @Fred, лучше всего использовать массивы. Вот небольшое объяснение и несколько советов по отладке.
Перед запуском тестов я добавил
echo "$PARAM"
set|grep '^PARAM='
чтобы на самом деле показать, что такое PARAM
.**
В вашем исходном тесте это:
PARAM=' --dry-run mirror.leaseweb.net::archlinux/ /tmp/test'
То есть это одна строка, содержащая несколько частей, разделенных пробелами.
Как правило (с исключениями!*
), bash разделяет слова, если вы не запретите это делать. В тестах A и C $@
без кавычек в __test1
дает bash возможность разделить $PARAM
. В тесте B $PARAM
без кавычек в вызове __test2has the same effect. Therefore,
rsync` видит каждый элемент, разделенный пробелом, как отдельный параметр в тестах A-C.
В тесте D "$PARAM"
, переданный __test2
, не разделяется при вызове __test2
из-за кавычек. Поэтому __test2
видит только один параметр в $@
. Затем внутри __test2
указанный в кавычках "$@"
сохраняет этот параметр вместе, поэтому он не разбивается на пробелы. В результате rsync
думает, что все PARAM
является именем хоста, поэтому терпит неудачу.
Если вы используете решение Фреда, вывод из sed|grep '^PARAM='
будет
PARAM=([0]="--dry-run" [1]="mirror.leaseweb.net::archlinux/" [2]="/tmp/test")
Это внутренняя нотация bash для массива: PARAM[0]
равно "--dry-run"
и т. д. Вы можете видеть каждое слово по отдельности. echo $PARAM
не очень полезно для массива, так как выводит только первое слово (здесь --dry-run
).
Правки
*
Как указывает Фред, одно исключение состоит в том, что в назначении A=$B
B
не будет расширено. То есть A=$B
и A="$B"
совпадают.
**
Как указывает Готи, вместо set|grep '^PARAM='
можно использовать declare -p PARAM
. встроенная функция объявления с переключателем -p
распечатает строку, которую вы можно вставить обратно в оболочку, чтобы воссоздать переменную. В этом случае этот вывод:
declare -a PARAM='([0]="--dry-run" [1]="mirror.leaseweb.net::archlinux/" [2]="/tmp/test")'
Это хороший вариант. Я лично предпочитаю подход set|grep
, потому что declare -p
дает вам дополнительный уровень цитирования, но оба варианта работают нормально. Изменить Как указывает @rici, используйте declare -p
, если элемент вашего массива может включать новую строку.
В качестве примера дополнительных кавычек рассмотрим unset PARAM ; declare -a PARAM ; PARAM+=("Jim's")
(новый массив с одним элементом). Тогда вы получите:
set|grep: PARAM=([0]="Jim's")
# just an apostrophe ^
declare -p: declare -a PARAM='([0]="Jim'\''s")'
# a bit uglier, in my opinion ^^^^
person
cxw
schedule
10.04.2017