如果我运行此 bash 命令并为语句添加前缀,则该变量fruit应该存在,但仅在此命令的持续时间内:
$ fruit=apple echo $fruit
$
Run Code Online (Sandbox Code Playgroud)
结果是一个空行。为什么?
引用通配符对此问题的评论:
参数扩展由shell完成,“fruit”变量不是shell变量;它只是“echo”命令环境中的一个环境变量
环境变量仍然是一个变量,所以这肯定对 echo 命令仍然可用吗?
jll*_*gre 12
问题是当前的 shell 过早地扩展变量;它没有在它的上下文中设置,所以echo命令没有任何参数,即命令最终是:
$ fruit=apple echo
Run Code Online (Sandbox Code Playgroud)
这是一种解决方法,其中变量不会因为单引号而过早扩展:
$ fruit=apple sh -c 'echo $fruit'
Run Code Online (Sandbox Code Playgroud)
或者,您也可以使用一行 shell 脚本来演示fruit变量已正确传递给执行的命令:
$ cat /tmp/echof
echo $fruit
$ /tmp/echof
$ fruit=apple /tmp/echof
apple
$ echo $fruit
$
Run Code Online (Sandbox Code Playgroud)
这个问题的一些评论引发了一些意想不到的争议和讨论:
fruit是否已经导出的事实不会影响行为,重要的是在 shell 扩展它的精确时刻变量值是什么。$出口水果=香蕉 $水果=苹果回声$水果 香蕉
echo命令是内置命令这一事实不会影响 OP 问题。但是,在某些情况下,使用具有此语法的内置函数或 shell 函数会产生意想不到的副作用,例如:$出口水果=香蕉 $fruit=apple eval 'echo $fruit' 苹果 $回声$水果 苹果
虽然这里提出的问题和那个问题有相似之处,但这并不是完全相同的问题。与其他问题,临时IFS变量的值尚未公布时,壳分词 的另一个变量$var,而在这里,临时fruit当shell变量值尚不可用扩展的相同的变量。
还有另一个问题,OP 询问所用语法的重要性,更准确地说是询问“为什么这样做?”。在这里 OP 意识到了重要性,但报告了意外行为并询问其原因,即“为什么这不起作用?”。好的,在更仔细地阅读另一个问题上发布的糟糕截图后,确实在那里描述了相同的情况 ( BAZ=jake echo $BAZ) 所以是的,毕竟这是一个重复
Wil*_*ard 12
为了正确理解这一点,让我们首先区分shell 变量和环境变量。
环境变量是所有进程都拥有的属性,无论它们是否在内部使用。即使sleep 10它正在运行也有环境变量。正如所有进程都有一个 PID(进程标识符)、一个当前工作目录 (cwd)、一个 PPID(父 PID)、一个参数列表(即使是空的)——等等。所有进程也都有所谓的“环境”,它在派生时从父进程继承。
从实用程序作者(用 C 编写代码的人)的角度来看,进程具有设置、取消设置或更改环境变量的能力。但是,从脚本作者的角度来看,大多数工具都没有为用户提供这种便利。相反,您可以使用shell更改进程环境,然后在执行您调用的命令(外部二进制文件)时继承该环境。(可以修改shell本身的环境并继承修改,或者您可以在fork之后但在执行您调用的命令之前指示shell进行修改。无论哪种方式,环境都是继承的。我们将同时查看两者方法。)
Shell变量是另一回事。尽管在 shell 中它们的行为方式相同,但不同之处在于仅仅“shell 变量”不会改变或影响您从shell调用的命令的行为。在适当的术语中,这种区别实际上会有所不同。导出的shell 变量将成为您调用的工具环境的一部分,而未导出的shell 变量则不会。不过,我觉得它更有利于通信指壳未导出为“shell变量”和shell变量变量被导出为“环境变量”,因为它们是 从 shell 派生的进程的角度来看环境变量。
这是很多文字。让我们看一些例子并描述发生了什么:
$ somevar=myfile
$ ls -l "$somevar"
-rw-r--r-- 1 Myname staff 0 May 29 19:12 myfile
$
Run Code Online (Sandbox Code Playgroud)
在这个例子中,somevar只是一个shell变量,没有什么特别之处。壳参数扩展(见LESS='+/Parameter Expansion' man bash发生时)之前的ls可执行实际被加载(“EXEC” ED)和ls命令(过程)甚至从未看到字符串“美元符号somevar”。它只看到字符串“myfile”,将其解释为当前工作目录中文件的路径,并获取并打印有关它的信息。
如果我们运行export somevar的前ls命令,那么事实somevar=myfile会出现在环境中的ls过程,但不会造成任何影响,因为该ls命令不会做与此变量什么。要查看环境变量的效果,我们必须选择一个环境变量,我们正在调用的进程将实际检查并使用该环境变量。
bc:基本计算器
可能有一个更好的例子,但这是我想出的一个并不太复杂的例子。首先你应该知道这bc是基本的计算器,处理和计算数学表达式。(在处理任何输入文件的内容后,它会处理其标准输入。我不会在我的示例中使用它的标准输入;我将只按 Ctrl-D,它不会显示在下面的文本片段中。另外我正在使用-q在每次调用时抑制介绍性消息。)
我将说明的环境变量描述于man bc:
BC_ENV_ARGS
This is another mechanism to get arguments to bc. The format is
the same as the command line arguments. These arguments are
processed first, so any files listed in the environment argu-
ments are processed before any command line argument files.
This allows the user to set up "standard" options and files to
be processed at every invocation of bc. The files in the envi-
ronment variables would typically contain function definitions
for functions the user wants defined every time bc is run.
Run Code Online (Sandbox Code Playgroud)
开始:
$ cat file1
5*5
$ bc -q file1
25
$ cat file2
6*7
8+9+10
$ bc -q file2
42
27
$ bc -q file1 file2
25
42
27
$
Run Code Online (Sandbox Code Playgroud)
这只是为了展示如何bc工作。在每种情况下,我都必须按 Ctrl-D 以向bc.
现在,让我们将环境变量直接传递给bc:
$ BC_ENV_ARGS=file1 bc -q file2
25
42
27
$ echo "$BC_ENV_ARGS"
$ bc -q file2
42
27
$
Run Code Online (Sandbox Code Playgroud)
请注意,稍后从命令中看不到我们放入该变量的内容echo。通过将赋值作为同一命令的一部分(也没有分号),我们将该变量赋值作为环境的一部分bc——它使我们正在运行的 shell 本身不受影响。
现在让我们设置BC_ENV_ARGS一个shell变量:
$ BC_ENV_ARGS=file1
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
42
27
$
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到我们的echo命令可以看到内容,但它不是环境的一部分,bc所以bc不能用它做任何特殊的事情。
当然,如果我们把变量本身放在bc的参数列表中,我们会看到一些东西:
$ bc -q "$BC_ENV_ARGS"
25
$
Run Code Online (Sandbox Code Playgroud)
但在这里,它是扩展变量的外壳,然后file1是实际出现在bc的参数列表中的内容。 所以这仍然将它用作shell变量,而不是环境变量。
现在让我们“导出”这个变量,使它既是一个 shell 变量又是一个环境变量:
$ export BC_ENV_ARGS
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
25
42
27
$
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到它file1是之前处理的file2,即使这里没有在命令行中提到。它是 shell 环境的一部分,并bc在您运行该进程时成为's 环境的一部分,因此该环境变量的值将被继承并影响bc操作方式。
我们仍然可以在每个命令的基础上覆盖它,甚至将其覆盖为空值:
$ BC_ENV_ARGS= bc -q file2
42
27
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
25
42
27
$
Run Code Online (Sandbox Code Playgroud)
但是正如您所看到的,变量在我们的 shell 中保持设置和导出,对 shell 本身和任何不覆盖该值的后续bc命令都是可见的。除非我们“取消导出”或“取消设置”它,否则它将保持这种状态。我会做后者:
$ unset BC_ENV_ARGS
$ echo "$BC_ENV_ARGS"
$ bc -q file2
42
27
$
Run Code Online (Sandbox Code Playgroud)
另一个例子,涉及产生另一个shell:
在您的 shell 中一个接一个地键入以下命令并考虑结果。看看您是否可以在运行之前预测结果。
# fruit is not set
echo "$fruit"
sh -c 'echo "$fruit"'
# fruit is set as a shell variable in the current shell only
fruit=apple
echo "$fruit"
sh -c 'echo "$fruit"'
sh -c "echo $fruit" ### NOT advised for use in scripts, for illustration only
# fruit is exported, so it's accessible in current AND new processes
export fruit
echo "$fruit"
sh -c 'echo "$fruit"'
echo '$fruit' ### I threw this in to make sure you're not confused on quoting
# fruit is unset again
unset fruit
echo "$fruit"
sh -c 'echo "$fruit"'
# setting fruit directly in environment of single command but NOT in current shell
fruit=apple sh -c 'echo "$fruit"'
echo "$fruit"
fruit=apple echo "$fruit"
# showing current shell is unaffected by directly setting env of single command
fruit=cherry
echo "$fruit"
fruit=apricot sh -c 'echo "$fruit"'
echo "$fruit"
sh -c 'echo "$fruit"'
Run Code Online (Sandbox Code Playgroud)
最后一个额外的技巧:你能预测以下命令的输出顺序吗?:)
fruit=banana
fruit=orange sh -c 'fruit=lemon echo "$fruit"; echo "$fruit"; export fruit=peach'
echo "$fruit"
Run Code Online (Sandbox Code Playgroud)
请在评论中提及任何需要澄清的内容;我确定这可以使用一些。但即使如此,它也应该有帮助。
| 归档时间: |
|
| 查看次数: |
3101 次 |
| 最近记录: |