在 bash 中,我如何定义“命令范围”变量?

Dan*_*lan 6 linux bash

我正在阅读有关 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)

如何定义具有命令范围的变量?

G-M*_*ca' 6

您的第一个猜测是正确的,但您使用了一个不好的例子。当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.shfoo等于运行命令bar,然后foo在返回到其主要输入(终端)时恢复到(如果有)的先前值。


use*_*686 5

这里的问题是变量扩展($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)