我在Windows 10和bash
上使用WSL(Ubuntu 18.04)。
我有一个文件filename.gpg
,内容如下:
export SOME_ENV_VAR='123'
现在我运行以下命令:
$ $(gpg -d filename.gpg)
$ echo $SOME_ENV_VAR
'123' <-- with quotes
但是,如果我直接在外壳中运行它:
$ export SOME_ENV_VAR='123'
$ echo $SOME_ENV_VAR
123 < -- without quotes
为什么会这样?为什么使用$()
运行命令和直接运行命令之间有区别?
此外:我使用eval $(gpg -d filename)
可以正常工作,我不知道为什么会这样。
答案 0 :(得分:4)
shell脚本中的报价与shell命令中的报价没有什么不同。
使用$(gpg -d filename.gpg)
语法,您不是在执行Shell脚本,而是执行常规的单个命令。
gpg -d filename.gpg
从以下实际示例中,您可以看到它与执行shell脚本有何不同:
SOME_ENV_VAR='123'
,这不能理解为变量赋值(您将获得SOME_ENV_VAR='123': command not found
)。export
)的参数。export SOME_ENV_VAR='123'
更改为export SOME_ENV_VAR=$PWD
,则SOME_ENV_VAR将不包含变量PWD的内容,而是字符串$var
分析命令时,请参见how bash performs expansion。
有很多步骤。 $(...)
被称为“命令替换”,是第四步。完成后,将不再执行之前的任何步骤。这就解释了为什么删除export
字时您的命令不起作用,以及为什么变量没有替换成结果。
此外,“引用删除”是最后一步,the manual reads:
所有未加引号的字符“ \”,“'”和“” 并非由于上述任一扩展而导致的结果
由于单引号是由“命令替换”扩展产生的,因此不会被删除。这就是SOME_ENV_VAR的内容为'123'
而不是123
的原因。
eval
为什么起作用?因为eval触发了其参数的另一个完整解析。整个扩展集将再次运行。
参数被合并为一个命令,然后被读取并执行
请注意,这意味着您仍在运行一个单个命令,而不是Shell脚本。如果您的filename.gpg
脚本有几行,则随后的行将被添加到第一个(也是唯一的)命令的参数列表中。
只需将source
与process substitution一起使用。
source <(gpg -d filename.gpg)
与eval
相反,source
用于在当前上下文中执行Shell脚本。进程替换提供了一个伪文件名,其中包含替换结果(即gpg
的输出)。