
Я пытаюсь понять расширения оболочки в 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".