переменная в bash, содержащая подстановку команд

переменная в bash, содержащая подстановку команд

Я пытаюсь понять расширения оболочки в bash (GNU bash, версия 4.4.20(1)-release (i686-pc-linux-gnu)).

Ввожу в интерактивную оболочку bash

x='$(id)'
$x
$(echo $x)

Я ожидал от любой из последних 2 строк ошибку вида

bash: uid=xxx(user): command not found

но получил

bash: $(id): command not found.

Я не понимаю, почему здесь не происходит подстановка команд. Разве это не должно быть реализовано после расширения переменной? Я предполагаю, что это связано с операциями Shell, как описано здесьhttps://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Operation

Может ли кто-нибудь объяснить такое поведение?

Мне просто интересно более точно понять расширения bash. Мне не интересно запускать реальный скрипт в моем вопросе.

решение1

$(…)является подстановкой команды (подстановка процесса <(…)и т. п.). Подстановка переменных и подстановка команд происходят в одном и том же проходе, слева направо в строке. Единственное, что происходит в результате этих подстановок, — это разделение слов и подстановка.

Так x='$(id)'устанавливается xв строку из 5 символов $(id). Затем, для запуска $x, оболочка заменяет $xзначением $(id). Оно не содержит пробелов или символов подстановки, поэтому рассматривается как имя команды.

Контраст с:

x='@(id)'
shopt -s extglob
echo /none/$x /usr/bin/$x

Если предположить, что файл /none/idне существует, но /usr/bin/idсуществует, echoкоманда расширяется до трех слов: echo(очевидно), /none/@(id)(шаблон глобуса /none/@(id)ничему не соответствует, поэтому он остается неизменным) и /usr/bin/id(шаблон глобуса /usr/bin/@(id)соответствует одному файлу, поэтому он заменяется списком совпадений из одного элемента).

В руководстве по bash соответствующее предложение находится в началеРасширения Shellраздел.

Порядок расширений следующий: расширение фигурных скобок; расширение тильды, расширение параметров и переменных, арифметическое расширение и подстановка команд (выполняется слева направо); разделение слов; и расширение имени файла.

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

Помните, что одно предложение (даже сложное, как то, что я процитировал выше) не может рассказать всю историю. Семантика оболочки запутана. Я сомневаюсь, что в руководстве по какой-либо оболочке есть подробности всех крайних случаев.Спецификация POSIXболее формален, но не охватывает специфичные для bash расширения и даже оставляет некоторые действительно странные случаи неопределенными.

решение2

Это кавычки. Одинарные кавычки ( ') определяют литеральную строку, и интерполяция или экранирование невозможны. Двойные кавычки ( ") допускают как интерполяцию, так и экранирование.

Вот пример:

$ x='$(id)'
$ echo 'The variable x contains the value \"$x\"'
The variable x contains the value \"$x\"
$ echo "The variable x contains the value \"$x\""
The variable x contains the value "$(id)"

$ x="$(id)"
$ echo "The variable x contains the value \"$x\""
The variable x contains the value "uid=1000(myusername)..."

Вот еще один пример интерполяции и экранирования на основе одинарных и двойных кавычек:

$ echo 'The current directory is $PWD according to the variable \"\$PWD\".'
The current directory is $PWD according to the variable \"\$PWD\".

$ echo "The current directory is $PWD according to the variable \"\$PWD\"."
The current directory is /tmp according to the variable "$PWD".

Связанный контент