linux - 为什么 shell 脚本中的引号与 shell 命令中的引号行为不同?

标签 linux bash shell ubuntu

我在 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

但是,如果我直接在 shell 中运行它:

$ export SOME_ENV_VAR='123'
$ echo $SOME_ENV_VAR
123 < -- without quotes

为什么会这样?为什么使用 $() 运行命令和直接运行命令有区别?

另外:我使用 eval $(gpg -d filename) 让它工作,我不知道为什么会这样。

最佳答案

shell 脚本中的引号与 shell 命令中的引号没有区别。

使用 $(gpg -d filename.gpg) 语法,您不是在执行 shell 脚本,而是在执行常规的单个命令。

你的命令做什么

  1. 它执行gpg -d filename.gpg
  2. 从结果中,它以第一个(IFS 分隔的)单词作为要执行的命令
  3. 它采用所有其他(IFS 分隔的)单词,包括来自其他行的单词,作为其参数
  4. 它执行命令

从下面的实际例子中,你可以看出它与执行shell脚本有何不同:

  1. 从 filename.gpg 中删除 export 一词:命令是 SOME_ENV_VAR='123',它不被理解为变量赋值(您将得到 SOME_ENV_VAR='123':命令未找到)。
  2. 如果您添加多行,它们将不会被理解为单独的命令行,而是被理解为第一个命令(export)的参数。
  3. 如果将 export SOME_ENV_VAR='123' 更改为 export SOME_ENV_VAR=$PWD,SOME_ENV_VAR 将不包含变量 PWD 的内容,而是包含字符串 $变量

为什么会这样?

参见 how bash performs expansion分析命令时。

有很多步骤。 $(...) 称为“命令替换”,是第四步。完成后,不会再执行前面的任何步骤。这解释了为什么当您删除 export 词时您的命令不起作用,以及为什么没有在结果中替换变量。

此外,“引用删除”是最后一步,the manual reads :

all unquoted occurrences of the characters ‘\’, ‘'’, and ‘"’ that did not result from one of the above expansions are removed

由于单引号是由“命令替换”扩展产生的,因此它们没有被删除。这就是为什么 SOME_ENV_VAR 的内容是 '123' 而不是 123

为什么 eval 有效?

因为eval触发了对其参数的又一次完整解析。再次运行整套扩展。

From the manual :

The arguments are concatenated together into a single command, which is then read and executed

请注意,这意味着您仍在运行一个 单个命令,而不是 shell 脚本。如果您的 filename.gpg 脚本有几行,后续行将添加到第一个(也是唯一一个)命令的参数列表中。

那我该怎么办?

只需使用 sourceprocess substitution .

source <(gpg -d filename.gpg)

eval相反,source用于在当前上下文中执行shell脚本。进程替换提供了一个包含替换结果的伪文件名(即 gpg 的输出)。

关于linux - 为什么 shell 脚本中的引号与 shell 命令中的引号行为不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54171835/

相关文章:

shell - 如何在不使用 `!!` 的情况下运行最后一个命令?

Ruby、bundle、SSH 和 Jenkins

linux - 如何检查重复文件,如果找到重复文件则追加文件名?

linux - 使用 SED 进行字符串替换

sql-server - 通过 isql 实用程序从 linux 客户端登录 sql server(windows 身份验证帐户)

C++ 在linux终端中运行命令我不明白

c - 我如何与 Cygwin 相处融洽?

regex - 如何使用 shell 脚本查找 Linux 发行版名称?

linux - 尝试比较 shell 脚本中的字符串时出错

linux - 如何在 bash 脚本和 Matlab 之间传递变量