我正在阅读有关 bash 架构的这本书,其中一位 bash 维护者说:
shell 函数调用有不同的变量作用域,命令前面的赋值语句设置的变量有不同的临时作用域。例如,当这些赋值语句位于 shell 内置的命令之前时,shell 必须跟踪解析变量引用的正确顺序,并且链接的作用域允许 bash 这样做。
我无法弄清楚如何做到这一点。这会打印出一个空白的换行符:
foo="bar" echo $foo
Run Code Online (Sandbox Code Playgroud)
这会打印出“bar”,但该foo变量在命令完成后仍被定义,所以我不认为它是“命令范围”:
foo="bar"; echo $foo
Run Code Online (Sandbox Code Playgroud)
如何定义具有命令范围的变量?
您的第一个猜测是正确的,但您使用了一个不好的例子。当shell读取命令时
foo=bar echo $foo
Run Code Online (Sandbox Code Playgroud)
它做的第一件事(或者至少是第一件事)是查找变量引用(如$foo)并评估它们。
然后它处理行的剩余部分。(这可能有点过于简单化;有关详细信息,请参阅bash(1)或Bash 参考手册。)所以您有了命令
回声(无)
运行foo=bar; 但为时已晚;没有什么可以查看 的值了$foo。您可以使用以下序列来演示这一点:
echo (nothing)
但
foo=bar命令
是要走的路。几个有效的例子是:
$ foo=oldvalue
$ foo=bar echo "$foo"
oldvalue
Run Code Online (Sandbox Code Playgroud)
和
foo=bar command
(您可能希望通过管道传输它grep foo。)
我刚刚注意到你引用的那句话,“例如,当那些赋值语句位于内置于 shell 的命令之前时,……”,暗示内置命令(如echo)代表一种特殊情况。直观(至少对我而言)您正在寻找的这个“命令范围”必须通过在子进程中设置环境变量来工作,并且不清楚它如何用于内置命令,它不在子进程中运行。直接访问环境的内置命令并不多,而且它们与 shell 变量的交互有时是神秘的。但我想出了更多的例子,它们可能更符合你的要求:
foo=bar sh -c 'echo "$foo"'
Run Code Online (Sandbox Code Playgroud)
将显示“bar”,因为 的评估$foo被推迟。它由eval(内置)命令处理,而不是 shell 的初始解析传递。或者,创建一个名为(例如)的文本文件showme.sh。将echo $foo(或echo "$foo")命令放入其中,然后说
foo=bar env
Run Code Online (Sandbox Code Playgroud)
这可能就是你的书所说的,“……shell 必须跟踪解析变量引用的正确顺序……”。不知何故,shellshowme.sh以foo等于运行命令bar,然后foo在返回到其主要输入(终端)时恢复到(如果有)的先前值。
这里的问题是变量扩展($foo→ 值)在评估整个命令(放入foo=bar环境,运行echo ...)之前发生。
它适用于访问环境本身的命令:
foo=bar env | grep ^foo
foo=bar declare -p foo
Run Code Online (Sandbox Code Playgroud)
Bash 并没有真正为您提供合适的范围;您将需要使用“子进程”技巧来为该命令创建一个新进程。(当然这意味着范围是只读的,没有任何更改会到达父级......)( ... )用于创建子shell:
(foo=bar; echo $foo)
Run Code Online (Sandbox Code Playgroud)