Answers:
这归结为评估工作原理的问题。这两个示例都以相同的方式工作,出现问题的原因在于shell(此处为bash)如何扩展变量。
编写此命令时:
HOME="foo" echo $HOME
该$HOME
扩展在运行命令之前。因此,它被扩展为原始值,而不是您为命令设置的新值。HOME
在echo
运行命令的环境中确实已更改了该变量,但是,您正在$HOME
从父级打印。
为了说明这一点,考虑一下:
$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon
如您在上方所见,第一个命令打印了临时更改的值,HOME
第二个命令打印了原始值,表明该变量只是临时更改了。由于bash -c ...
命令用单引号(' '
)而不是双引号()引起来" "
,因此该变量不会扩展,而是按原样传递给新的bash进程。然后,此新过程将其展开并打印已设置为的新值。如果使用,您会看到这种情况set -x
:
$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello
+ echo hello
hello
如您在上方所见,变量 $HOME
从未传递给echo
。它只会看到其扩展的价值。与之比较:
$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello
在这里,由于使用单引号,因此将变量而不是其值传递给新进程。
当shell解析一行时,它将把该行标记为单词,对单词执行各种扩展(按顺序),然后执行命令。
假设 test=1:2:3:4:5:6
让我们看一下以下命令: IFS=":" read a b c d e f <<< "$test"
分词后,将发生参数扩展:IFS=":"
read
a
b
c
d
e
f
<<<
"1:2:3:4:5:6"
Shell将在读取命令期间设置IFS变量,并read
知道如何将$ IFS应用于其输入,并为变量名称提供值。
此命令的故事类似,但结果不同: HOME="hello" echo "$HOME"
由于参数扩展发生在命令开始之前,因此shell具有:
HOME="hello" echo "/home/username"
然后,在执行echo命令期间,根本不使用$ HOME的新值。
要实现您要执行的操作,请选择以下一项
# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'
要么
# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")
但不要选择第一个。
eval
避免该解决方案?
有两个作用域:环境变量和局部变量。环境变量的有效期为每一个过程(见setenv
,getenv
),而局部变量只能在这个shell会话是活动的。(这不是明显的区别...)
暗示的env
(如您的示例所示)修改环境,而echo ...
使用本地环境-因此env
无效。
例如,要修改局部变量,请使用
( HOME="foo" ; echo "$HOME" )
在此,括号定义了此分配的范围。
local
。