bash 脚本在命令上的临时值

bash 脚本在命令上的临时值

就像下面的命令一样,

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

有两个作用域:环境变量和局部变量。环境变量对每个进程都有效(请参阅setenvgetenv),而局部变量仅在该 shell 会话中有效。 (这不是一个明显的区别...)

隐含env(如您的示例中)修改环境,同时echo ...使用本地环境 - 因此env没有效果。

要修改局部变量,请使用:

( HOME="foo" ; echo "$HOME" )

这里的括号定义了该赋值的范围。

相关内容