我的一位同事向我提供了一种我不熟悉的 Bash 语法。我的 Google foo 没能弄清楚它的作用以及为什么/何时应该使用它。
他发给我的命令是这样的:
someVariable=something command
最初,我认为这相当于以下内容:
someVariable=something ; command
或者
someVariable=something
command
但事实似乎并非如此。例子:
[Jan-03 11:26][~]$ # Look at the environment variable BAZ. It is currently empty
[Jan-03 11:26][~]$ echo $BAZ
[Jan-03 11:27][~]$ # Try running a command of the same format
[Jan-03 11:27][~]$ BAZ=jake echo $BAZ
[Jan-03 11:27][~]$
[Jan-03 11:27][~]$ # Now, echo BAZ again. It is still empty:
[Jan-03 11:27][~]$ echo $BAZ
[Jan-03 11:27][~]$
[Jan-03 11:28][~]$
[Jan-03 11:28][~]$ # If we add a semi-colon to the command, we get dramatically different results:
[Jan-03 11:28][~]$ BAZ=jake ; echo $BAZ
jake
[Jan-03 11:28][~]$
[Jan-03 11:28][~]$ # And we can see that the variable is actually set:
[Jan-03 11:29][~]$ echo $BAZ
jake
[Jan-03 11:29][~]$
这个语法有什么作用?已设置的变量会发生什么情况?为什么这有效?
答案1
这相当于:
( export someVariable=something; command )
这将创建someVariable
一个具有指定值的环境变量,但仅适用于正在运行的命令。
以下是手册的相关部分bash
:
简单命令
一个简单的命令是一个序列可选变量赋值后面是空格分隔的单词和重定向,并由控制运算符终止。第一个字指定要执行的命令,并作为参数零传递。其余单词作为参数传递给调用的命令。
(...)
简单的命令扩展
如果[命令扩展]没有产生命令名,则变量分配会影响当前的 shell 环境。否则,变量被添加到执行命令的环境中,不会影响当前的 shell 环境。
注意:请记住,这并不特定于bash
,但是由 POSIX 指定。
编辑 - 从答案中的评论中总结讨论
BAZ=JAKE echo $BAZ
不打印 JAKE 的原因是因为变量替换是在其他任何事情之前完成的。如果您绕过变量替换,则会按预期运行:
$ echo_baz() { echo "[$BAZ]"; }
$ BAZ=Jake echo_baz
[Jake]
$ echo_baz
[]
答案2
这些是上下文中的变量赋值简单的命令。作为希安已经提到,对于外部命令,它们相当于在命令持续时间内导出分配的值。
在您的示例中,您使用的是内置命令,因此行为并不完全相同:分配会影响当前环境,但未指定执行内置命令后效果是否持续。要理解您的示例,您需要知道参数扩展发生在处理变量之前;因此与
BAZ=jake echo $BAZ
shell 首先展开$BAZ
(没有任何结果),然后设置BAZ
为jake
,最后运行
echo
它打印一个空行。 (然后 shell 会忘记,BAZ
如后续echo $BAZ
所示。)
BAZ=jake; echo $BAZ
被解释为两个命令:首先BAZ
在当前环境中设置变量,然后echo $BAZ
扩展echo jake
并执行。
答案3
这里发生了一些关键的事情:
正如中所解释的bash 参考手册,简单命令扩展,“如果没有命令名结果,则变量分配会影响当前 shell 环境。否则,变量被添加到执行命令的环境中并且不影响当前的 shell 环境。”因此,当您说 时
var="something" command arg1 arg2
,该命令将var
在命令环境中运行并在command
退出后消失。这一点的演示很简单 - 从命令中访问命令环境:$ BAZ="jake" python -c "import os; print os.environ['BAZ']" jake
这不足为奇。这种语法经常用于在修改过的环境下运行程序。我的 unix.stackexchange.com 和 askubuntu.com 用户会认识这个示例:如果用户使用德语语言环境,而您只说英语,您可以要求他们重现遇到的任何问题并获取英语输出,如下所示:
LC_ALL=C command
另请注意,环境访问并不特定于
python
.它可以使用C
或任何其他编程语言来完成。它恰好是我现在的“选择武器”,并且仅用于此演示。变量扩展发生在任何运行之前。因此,当您运行类似的命令时
BAZ="foo" echo $BAZ
,shell 首先查看其环境,发现那里没有变量 BAZ,因此留空$BAZ
。简单的演示是:$ BAZ="jake" python -c "import sys; print 'ARG:',sys.argv[1]" $BAZ ARG: Traceback (most recent call last): File "<string>", line 1, in <module> IndexError: list index out of range
还需要注意的是,其中的区别BAZ=jake; echo $BAZ
是两个单独的命令语句,这里BAZ=jake
将保留在 shell 的环境中。用例取决于您的意图。如果您打算运行多个需要此类变量的程序,则可能需要export
此类变量。如果您仅在这一特定时间需要它,那么前面的变量分配可能会更好。
答案4
简单来说:
BAZ=jake echo $BAZ
不输出任何内容,因为 ,variable substitution
在命令中首先发生。这意味着,此命令行上发生的第一件事$BAZ
将被替换为BAZ
先前定义的变量的任何实际值(如果有)。请注意,这种情况甚至发生在 shell 考虑BAZ=jake
同一命令行之前。
在执行我们的命令之前,sinceBAZ
没有分配任何值,并且since仅在解析BAZ=jake
后才被考虑,因此不会解析为任何值。因此不会输出任何内容。$BAZ
$BAZ
echo $BAZ
BAZ=jake
只是给定命令的一部分(shell 不会将其视为/设置为环境变量)。如果某些进程作为同一命令行的一部分执行,则使用此变量,这非常有用BAZ
。一旦命令执行完成,BAZ
,的值就不稳定。jake
例如:]# LD_LIBRARY_PATH="new_path" ldconfig
, 在哪里ldconfig
命令内部引用变量LD_LIBRARY_PATH
,并且与上面不同的是,该命令行中没有变量扩展。
对于其他情况:
BAZ=jake; echo $BAZ
它们是两个不同的命令,在一行中给出。这和执行一项又一项任务一样好。