Как сопоставляются двойные кавычки в bash (в паре)?

15

Я использую GNU bash 4.3.48. Рассмотрим следующие две команды, которые отличаются только одним знаком доллара.

Команда 1:

echo "(echo " * ")"

Команда 2:

echo "$(echo " * ")"

На выходе их соответственно

(echo  test.txt ppcg.sh )

и

 * 

Таким образом, очевидно, что в первом случае *символ сглаживается, что означает, что первая кавычка идет со вторым, образуя пару, а третий и четвертый образуют другую пару.

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

Есть ли какие-либо другие случаи, кроме $()конструкции, в которой кавычки не сопоставляются со следующей, а вместо этого вкладываются? Хорошо ли задокументировано это поведение, и если да, где я могу найти соответствующий документ?

Вейцзюнь Чжоу
источник
2
Связанный: Bash кавычки не экранированы при замене команды .
G-Man говорит: «Восстановите Монику»
Еще раз, подсветка синтаксиса в UL SE неверна и вводит в заблуждение, хотя виноват JS.
Вейцзюнь Чжоу

Ответы:

14

Любые из вложенных конструкций, которые могут быть интерполированы внутри строк, могут иметь дополнительные строки внутри них: они анализируются как новый скрипт, вплоть до закрывающего маркера, и могут даже быть вложенными на нескольких уровнях глубиной. Все строки одного из них начинаются с $. Все они документированы в сочетании руководства Bash и спецификации языка команд оболочки POSIX.

Есть несколько случаев этих конструкций:

  • Подстановка команды$( ... ) , как вы нашли. POSIX определяет это поведение :

    В $(command)форме все символы, следующие за открывающей скобкой и совпадающей с закрывающей скобкой, составляют команду. Любой действительный сценарий оболочки может быть использован для команды ...

    Кавычки являются частью действительных сценариев оболочки, поэтому они допускаются с их обычным значением.

  • Подстановка команд с использованием `тоже.
  • Элемент "word" в экземплярах с расширенной заменой параметров, таких как${parameter:-word} . Определение «слова» является :

    Последовательность символов, рассматриваемая оболочкой как единое целое

    - который включает цитируемый текст и даже смешанные кавычки a"b"c'd'e- хотя фактическое поведение расширений немного более либерально, чем, например, ${x:-hello world}тоже работает.

  • Арифметическое расширение с помощью $(( ... )), хотя оно там в значительной степени бесполезно (но вы также можете вкладывать подстановки команд или расширения переменных, а затем использовать в них кавычки). POSIX заявляет, что :

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

    так что это поведение явно требуется. Это означает, что echo "abc $((4 "*" 5))"делает арифметику, а не скупость.

    Обратите внимание, что $[ ... ]арифметическое расширение старого стиля не обрабатывается одинаково: кавычки будут ошибкой, если они появятся, независимо от того, указано ли расширение в кавычках или нет. Эта форма больше не документирована и не предназначена для использования в любом случае.

  • Специфичный для локали перевод с$"..." , который фактически использует в "качестве основного элемента. $"рассматривается как единое целое.

Есть еще один случай вложенности, который вы можете не ожидать, не включая кавычки, который связан с расширением скобки : {a,b{c,d},e}расширяется до "a bc bd e". ${x:-a{b,c}d}делает не гнездо, однако; он рассматривается как подстановка параметра, давая " a{b,c", а затем " d}". Это также задокументировано :

Когда используются фигурные скобки, соответствующая конечная фигурная скобка - это первая '}', которая не экранируется обратной косой чертой или строкой в ​​кавычках и не находится во встроенном арифметическом расширении, подстановке команд или расширении параметра.


Как правило, все конструкции с разделителями анализируют свои тела независимо от окружающего контекста (а исключения рассматриваются как ошибки ). В сущности, при просмотре $(кода подстановки команд просто просит синтаксический анализатор извлечь из тела все, что он может, как если бы это была новая программа, а затем проверяет, что ожидаемый завершающий маркер (неэкранированный )или ))или }) появляется после запуска подпарсера. из вещей, которые он может потреблять.

Если вы думаете о функционировании синтаксического анализатора с рекурсивным спуском , это просто простая рекурсия к базовому случаю. На самом деле это проще сделать, чем другим способом, если у вас вообще есть интерполяция строк. Независимо от основной техники синтаксического анализа, оболочки, поддерживающие эти конструкции, дают одинаковый результат.

Вы можете вкладывать цитаты настолько глубоко, насколько захотите, с помощью этих конструкций, и они будут работать, как и ожидалось. Нигде не запутаться, увидев цитату в середине; вместо этого это будет началом новой строки в кавычках во внутреннем контексте.

Майкл Гомер
источник
Благодарю. В "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", почему это вторая двойная кавычка не конец первой двойной кавычки (как показано подсветка синтаксиса в вашем ответе), но начало строки внутри $(...)? Это потому, что парсер bash работает сверху вниз, а не снизу вверх?
StackExchange для всех
2
Существует много вариантов обработки "${var-"foo"}"(например echo "${-+"*"}", то же самое, что и echo *в оболочке Bourne или Korn), и в следующей версии стандарта поведение будет четко определено . См. Также обсуждение на mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Стефан
3

Возможно, рассмотрение двух примеров с printf(вместо echo) поможет:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

Он печатает (echo (первое слово, включая завершающий пробел), некоторые файлы и заключительное слово ).
Скобки - это просто часть строки в кавычках (echo .
Звездочка (теперь без кавычек, так как две двойные кавычки в паре) разворачивается как глобус в список подходящих файлов.
И затем закрывающая скобка.

Однако ваша вторая команда работает следующим образом:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

$Начинает замену команды. Это начинается свежая цитата.
Звездочка " * "заключена в кавычки, и именно это echoвыводит команда (здесь это команда, а не строка в кавычках) . Наконец, printfповторно форматирует *и печатает как < * >.

Исаак
источник