Bash:在 $() 命令替换中转义双引号

Bash:在 $() 命令替换中转义双引号

当命令的部分参数来自变量时,我无法理解命令替换的工作原理。

为了说明这一点,我将仅提供一系列命令:

我将首先A B C在 tmp 目录中创建一个目录,在新目录中我将创建一个新文件abcfile

user@desktop /tmp
$ pwd
/tmp

user@desktop /tmp
$ mkdir "A B C"

user@desktop /tmp
$ ls
'A B C'

user@desktop /tmp
$ touch "A B C"/abcfile

user@desktop /tmp
$ ls "A B C"/
abcfile

我将新地将值分配A B C给变量$DIR,并尝试使用ls调用命令的各种方式来调用以列出该目录的内容

user@desktop /tmp
$ DIR="A B C"

当变量不加引号时,命令会按预期失败

user@desktop /tmp
$ ls $DIR
ls: cannot access 'A': No such file or directory
ls: cannot access 'B': No such file or directory
ls: cannot access 'C': No such file or directory

引用变量后,它按预期工作

user@desktop /tmp
$ ls "$DIR"
abcfile

如果我想要命令的输出作为字符串

user@desktop /tmp
$ echo $(ls "$DIR")
abcfile

这个命令有效,但是为什么它有效呢?根据 shellcheck 这实际上是不正确的,变量应该在双引号之外

user@desktop /tmp
$ echo "$(ls "$DIR")"
abcfile

如果内部变量没有双引号,则命令失败

user@desktop /tmp
$ echo "$(ls $DIR)"
ls: cannot access 'A': No such file or directory
ls: cannot access 'B': No such file or directory
ls: cannot access 'C': No such file or directory

答案1

我假设您知道输出命令的输出不需要使用asecho "$(somecommand)"来替换,并且您使用此构造只是作为示例。somecommandecho

该命令echo $(ls "$DIR")“有效”是因为ls "$DIR"输出abcfileecho输出。

在这种情况下,在这里引用命令替换不会产生任何影响。该字符串abcfile不需要引号,除非您更改$IFS为包含该文件名中存在的字符(请参阅下面的原因)。

然而,考虑

A B C/
|-- A*
`-- abcfile

现在:

$ echo "$(ls "$DIR")"
A*
abcfile
$ echo $(ls "$DIR")
A B C abcfile

您会注意到最后一个输出将文件名扩展A*为文件名通配模式,它与目录的名称匹配A B C。您还会注意到我们丢失了输出的换行符ls

换行符丢失是因为 shell 丢失了分词在 的未加引号的输出上ls,将其拆分为空格、制表符和换行符上的单词( 的默认内容$IFS)。

插入目录名A B C是因为分词生成的单词经过了文件名生成(通配符)。

如果新文件的名称为A* A*,则目录的名称将被插入两次:

A B C/
|-- A* A*
`-- abcfile
$ echo $(ls "$DIR")
A B C A B C abcfile

有关的:

答案2

看来这是主要的误解:

变量应该在双引号之外

user@desktop /tmp
$ echo "$(ls "$DIR")"
abcfile

该变量不在双引号之外。 shell考虑在里面引用$() 分别地而不是在外面引用。这意味着echo "$(ls "$DIR")"双引号中实际上是嵌套的:

echo "$(ls "$DIR")"
#          ^    ^     inner quotes
#    ^            ^   outer quotes

因为一般来说双引号变量是好的整个$()陈述(除非你知道你不想引用),你需要这些引用中的每一个。

比较“怪癖2”这个答案


附带问题:

如果我想要命令的输出作为字符串

user@desktop /tmp
$ echo $(ls "$DIR")
abcfile

输出到 stdout 没有类型,echo不会使其成为字符串。在这种特殊情况下,echo什么都不改变,但请参阅出什么问题了echo $(stuff)

相关内容