Почему bash проглатывание -e перед массивом

Учитывая следующий синтаксис:

x=(-a 2);echo "${x[@]}";x=(-e 2 -e); echo "${x[@]}"

Вывод:

-a 2
2 -e

Требуемый вывод

-a 2
-e 2 -e

Почему это происходит? Как я могу исправить?

Ответ 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[*]}"

Ответ 2

Хороший! Что происходит, первый -e интерпретируется как опция для эха (чтобы включить escape-последовательности) Обычно вы делаете что-то вроде echo -- "-e", и оно должно просто печатать -e, но echo с удовольствием ведет себя по-другому и просто выводит -- -e как целую строку.

echo не интерпретирует - значит конец опций.

Решение проблемы также можно найти на страницах руководства:

Из-за псевдонимов оболочки и встроенной команды echo, используя незакрашенные echo в интерактивном режиме или в script может получить различную функциональность чем описано здесь. Вызовите его через env (т.е. env echo ...) чтобы избежать помех от оболочки.

Итак, что-то вроде этого должно работать:

x=(-a 2);echo "${x[@]}";x=(-e 2 -e); env echo "${x[@]}"