Ответ 1
TL;DR
printf "%s\n" "${x[*]}"
Описание
echo
принимает 3 варианта:
$ help echo
[…]
Options:
-n do not append a newline
-e enable interpretation of the following backslash escapes
-E explicitly suppress interpretation of backslash escapes
Итак, если вы запустите:
$ echo -n
$ echo -n -e
$ echo -n -e -E
Вы ничего не получите. Даже если вы поместите каждую опцию в кавычки, она по-прежнему выглядит так же, как и bash:
$ echo "-n"
$ echo "-n" "-e"
Последняя команда запускает echo
с двумя аргументами: -n
и -e
. Теперь сравните это с:
$ echo "-n -e"
-n -e
Мы выполнили echo
с одним аргументом: -n -e
. Поскольку bash не распознает (комбинированный) вариант -n -e
, он, наконец, перекликается с единственным аргументом терминала, как мы хотим.
Применяется к массивам
Во втором случае массив x
начинается с элемента -e
. После того, как bash расширяет массив ${x[@]}
, вы эффективно выполняете:
$ echo "-e" "2" "-e"
2 -e
Поскольку первый аргумент -e
, он интерпретируется как опция (вместо эхо-сигнала для терминала), как мы уже видели.
Теперь сравним это с другим стилем расширения массива ${x[*]}
, который эффективно выполняет следующее:
$ echo "-e 2 -e"
-e 2 -e
bash видит единственный аргумент -e 2 -e
- и поскольку он не распознает это как опцию - он перекликается с аргументом терминала.
Обратите внимание, что ${x[*]}
расширение стиля вообще не безопасно. Возьмем следующий пример:
$ x=(-e)
$ echo "${x[*]}"
Ничего не печатается, хотя мы ожидали, что -e
будет отображаться эхом. Если вы обратили внимание, вы уже знаете, почему это так.
Экранирование
Решение состоит в том, чтобы избежать любых аргументов команды echo
. К сожалению, в отличие от других команд, которые предлагают какой-то способ сказать: "Эй, следующий аргумент не должен интерпретироваться как опция" (как правило, аргумент --
), bash не обеспечивает такого экранирования механизм для echo
.
К счастью, существует команда printf
, которая обеспечивает надстройку функциональности, предлагаемой echo
. Следовательно, мы приходим к решению:
printf "%s\n" "${x[*]}"