就像下面的命令一样,
if true; then
IFS=":" read a b c d e f <<< "$test"
书上说,当赋值命令(IFS ":"
)在主命令( )之前使用时read a b c d e f <<< "$value"
,其值暂时对主命令有效。因此,该read
命令使用分隔符:
。
但是,就像这个命令一样,
if true; then
HOME="hello" echo "$HOME"
回显消息不是你好。上述命令的真正含义是什么?
答案1
这归结为评估如何进行的问题。这两个示例的工作方式相同,问题的发生是由于 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 /home/terdon
/home/terdon
正如您在上面看到的,多变的 $HOME
从未传递到echo
.它只看到了其扩展的价值。与之比较:
$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello
在这里,由于单引号,变量而不是它的值被传递给新进程。
答案2
当 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")
但不要选择第一个。
答案3
有两个作用域:环境变量和局部变量。环境变量对每个进程都有效(请参阅setenv
、getenv
),而局部变量仅在该 shell 会话中有效。 (这不是一个明显的区别...)
隐含env
(如您的示例中)修改环境,同时echo ...
使用本地环境 - 因此env
没有效果。
要修改局部变量,请使用:
( HOME="foo" ; echo "$HOME" )
这里的括号定义了该赋值的范围。