我试图了解 bash 中的 shell 扩展(GNU bash,版本 4.4.20(1)-release (i686-pc-linux-gnu))。
在我的交互式 bash shell 中输入
x='$(id)'
$x
$(echo $x)
我预计最后两行中的任何一行都会出现以下形式的错误
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
,shell 将替换$x
为值$(id)
。它不包含任何空格或通配符,因此它被视为命令名称。
对比:
x='@(id)'
shopt -s extglob
echo /none/$x /usr/bin/$x
假设该文件/none/id
不存在但/usr/bin/id
确实存在,则该echo
命令将扩展为三个单词:(echo
显然)、/none/@(id)
(glob 模式/none/@(id)
不匹配任何内容,因此保持不变)和/usr/bin/id
(glob 模式/usr/bin/@(id)
匹配一个文件,因此将其替换为匹配的单元素列表)。
在bash手册中,相关的句子位于外壳扩展部分。
展开的顺序是:大括号展开;波形符扩展、参数和变量扩展、算术扩展和命令替换(以从左到右的方式完成);分词;和文件名扩展。
两个分号之间的所有内容都是一次传递。每一遍都基于前一遍的结果。
请注意,单个句子(即使是像我上面引用的那样复杂的句子)无法讲述整个故事。 shell 语义很复杂。我怀疑任何 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".