我正在做一些非常简单的事情:v=5 echo "$v"
并期望它打印5
。但事实并非如此。刚刚设置的值不适用于下一个命令。
我recently learnt" 在大多数shell中,管道的每个命令都在一个单独的SubShell中执行"。但是,在这种情况下,两个命令都在同一个子shell中执行。
为什么会这样?有没有办法让这个工作?
完整示例:
$ v=1
$ v=5 echo "$v"
1 # I expected 5!
答案 0 :(得分:6)
简单地说,在调用命令之前评估“$ v”,而在命令前面加上“v = 5”会修改正在运行的命令的环境。
正如Charles Duffy所说,你可以添加一个中间的“sh”过程,它会用类似的语法评估变量,但是你可能想要做一些更复杂的事情,知道如果你还有什么是有用的麻烦。
答案 1 :(得分:4)
让我们看一下the POSIX specification来理解为什么这样做,不仅仅是在bash中,而是在任何兼容的shell中:
根据规则7(b),涵盖分配在简单命令之前的情况:
如果'='前面的所有字符都形成有效名称(参见IEEE Std 1003.1-2001的基本定义卷,第3.230节,名称),则应返回令牌ASSIGNMENT_WORD。 (引用的字符不能参与形成有效名称。)
[...]
NAME的分配应按照简单命令中的规定进行。
因此,对于符合POSIX的shell,需要解析此赋值。
重定向应按重定向中所述执行。
- 醇>
在分配值之前,应扩展每个变量赋值以进行波浪扩展,参数扩展,命令替换,算术扩展和引用删除。
[...]
如果没有命令名称结果,变量赋值将影响当前的执行环境。否则,应为命令的执行环境导出变量赋值,并且不应影响当前的执行环境(特殊内置函数除外)。如果任何变量赋值尝试赋值对于只读变量,应发生变量赋值错误。请参阅Shell错误的后果,了解这些错误的后果。
因此:必须导出在简单命令的前缀的一部分中给出的赋值,并且不得影响“当前shell环境”,除非被调用的shell是特殊的内置函数。此外,这些步骤应遵循重定向,这些重定向本质上必须在命令调用过程的后期发生。
特殊内置函数以外的实用程序(请参阅特殊内置实用程序)应在包含以下内容的单独环境中调用。除非如下所述,否则这些对象的初始值应与父shell的初始值相同。
[...]
具有导出属性的变量以及在命令持续时间内显式导出的变量应传递给实用程序环境变量
因此:这些变量在fork之后和执行命令被调用之前由子shell扩展,并且必须 - 通过规范 - 仅影响子环境。
现在,对于某些不同的行为:
v=5 sh -c 'echo "$v"'
...受益于sh
实例在启动时从其环境变量创建shell变量(如POSIX规范第2.5.3节所述)。
顺便说一句,值得注意的是,您要求的语法是在简单命令中进行分配,而不是在子shell 中进行分配。您可以控制管道中涉及的子shell中的赋值,如下所示:
{ v=5; echo "$v"; } | somecommand ...
...它将赋值放入运行管道的第一个组件的子shell中(如果你的shell确实在子shell中运行该组件,这是未定义的行为,因为POSIX是关注的;来自规范: “但是,作为扩展,管道中的任何或所有命令都可以在当前环境中执行”)。
答案 2 :(得分:2)
v=5 echo $v
将v = 5添加到环境变量中,然后执行echo $v
。 $v
引用了在第一行中设置为1的shell变量v
。
添加半冒号v=5;echo $v
将shell变量设置为5,然后在分号后执行命令,即echo $v
,并生成5。
尝试此命令:
v=5 env|less
并查看环境。