A.C*_*Cho 13 bash environment-variables
像下面的命令,
if true; then
IFS=":" read a b c d e f <<< "$test"
Run Code Online (Sandbox Code Playgroud)
书中说,当赋值命令(IFS ":")在主命令(read a b c d e f <<< "$value")之前使用时,它的值暂时对主命令有效。因此,该read命令使用 delimiter :。
但是,就像这个命令,
if true; then
HOME="hello" echo "$HOME"
Run Code Online (Sandbox Code Playgroud)
回声消息不是你好。上述命令的真正含义是什么?
当 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"
Run Code Online (Sandbox Code Playgroud)
然后,在执行 echo 命令期间,根本不使用 $HOME 的新值。
要实现您的目标,请选择其中之一
# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'
Run Code Online (Sandbox Code Playgroud)
或者
# 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")
Run Code Online (Sandbox Code Playgroud)
但不要选择第一个。
这归结为评估如何运作的问题。两个示例都以相同的方式工作,问题的发生是因为 shell(这里是 bash)如何扩展变量。
当您编写此命令时:
HOME="foo" echo $HOME
Run Code Online (Sandbox Code Playgroud)
该$HOME扩展在运行命令之前。因此,它会扩展为原始值,而不是您为命令设置的新值。该HOME变量确实在echo命令运行的环境中发生了变化,但是,您正在$HOME从父级打印。
为了说明,请考虑:
$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon
Run Code Online (Sandbox Code Playgroud)
如上所示,第一个命令打印临时更改的值,HOME第二个命令打印原始值,证明变量只是临时更改的。由于该bash -c ...命令用单引号 ( ' ') 而不是双引号 ( )括起来" ",因此变量不会被扩展并按原样传递给新的 bash 进程。这个新进程然后扩展它并打印它已设置的新值。如果您使用,您可以看到这种情况set -x:
$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello
+ echo /home/terdon
/home/terdon
Run Code Online (Sandbox Code Playgroud)
正如您在上面看到的,变量 $HOME永远不会传递给echo. 它只看到其扩展的价值。与之比较:
$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello
Run Code Online (Sandbox Code Playgroud)
在这里,由于单引号,变量而不是它的值被传递给新进程。