让我们看一下POSIX 规范,以了解为什么它的行为如此,不仅在 bash 中,而且在任何兼容的 shell 中:
2.10.2、Shell 语法规则
从规则 7(b) 开始,涵盖了赋值在简单命令之前的情况:
如果 '=' 之前的所有字符构成一个有效名称(参见 IEEE Std 1003.1-2001 的基本定义卷,第 3.230 节,名称),则应返回令牌 ASSIGNMENT_WORD。(带引号的字符不能参与形成有效名称。)
[...]
名称的分配应按照简单命令中的规定进行。
因此,对于 POSIX 兼容的 shell,需要解析这个赋值。
2.9.1、简单命令
重定向应按照重定向中的描述执行。
在分配值之前,每个变量分配都应针对波浪号扩展、参数扩展、命令替换、算术扩展和引号删除进行扩展。
[...]
如果没有产生命令名,变量赋值将影响当前的执行环境。否则,为命令的执行环境导出变量赋值,并且不影响当前执行环境(特殊内置除外)。如果任何变量赋值尝试为只读变量赋值,则会发生变量赋值错误。有关这些错误的后果,请参阅 Shell 错误的后果。
因此: 必须导出在部分前缀中给简单命令的赋值,并且不得影响“当前 shell 环境”,除非被调用的命令是特殊的内置命令。此外,这些步骤应遵循重定向,这本质上必须在命令调用过程的后期发生。
2.12、Shell执行环境
应在包含以下内容的单独环境中调用除特殊内置程序(参见特殊内置实用程序)以外的实用程序。这些对象的初始值应与父外壳的初始值相同,但以下说明除外。
[...]
具有导出属性的变量,以及在命令期间显式导出的变量,应传递给实用程序环境变量
因此:这些变量在 fork 之后和执行被调用的命令之前由子 shell 扩展,并且必须 - 根据规范 - 单独影响孩子的环境。
现在,对于一些不同的行为:
SOMEVAR=BBB sh -c 'echo "$SOMEVAR"'
...受益于sh
实例在启动时从其环境变量(根据 POSIX 规范第 2.5.3 节的要求)创建 shell 变量。
顺便说一句,值得注意的是,您所询问的语法是在一个简单的 command中进行赋值,而不是在subshell中进行赋值。您可以控制管道中涉及的子shell中的分配,如下所示:
{ SOMEVAR=BBB; echo "$SOMEVAR"; } | somecommand ...
...将分配放入运行管道的第一个组件的子外壳中(如果您的外壳确实在子外壳中运行该组件,就POSIX而言,这是未定义的行为;从规范中:“作为扩展,但是,管道中的任何或所有命令都可以在当前环境中执行”)。