POSIX shell 中的命令替换后会删除引号吗?
does quote removal happen after command substitution in a POSIX shell?
POSIX shell 标准
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04
在第 2.6 节中说:
command substitution (...) shall be performed
(...)
Quote removal (...) shall always be performed last.
在我看来,在命令替换后没有执行引号删除:
$ echo "#"
#
$ echo '"'
"
符合预期,但是
$ echo $(echo '"')#"
>
我哪里不明白?
阅读后添加answer/comments:
按大家的说法,引号的考虑发生在解析的最开始,例如判断一个命令是否为偶数"acceptable"。那么为什么标准要强调,引用 删除 是在过程的后期执行的??
"then the outer command becomes echo "#"
and is balanced"
那不是 'balanced' 因为第一个双引号 不算 。引号只有在命令行中不受阻碍地出现时才有意义。
为了验证,让我们看一下:
$ echo $(echo '"')#
"#
这是平衡的,因为 shell 确实认为 "
只是另一个字符。
相比之下,这是不平衡的,因为它只有一个 shell-active "
:
$ echo $(echo '"')#"
>
类似例子1
这里我们展示了同样的东西,但是使用了参数扩展而不是命令替换:
$ q='"'; echo $q
"
一旦 shell 将 "
替换为 $q
,人们可能会认为存在不平衡的双引号。但是,那个双引号是 参数扩展 的结果,因此 不是 一个 shell-active 引号。
类似例子2
让我们考虑一个包含 file
:
的目录
$ ls
file
$ ls "file"
file
正如您在上面看到的,引号删除是在 ls
为 运行 之前执行的。
但是,考虑这个命令:
$ echo ls $(echo '"file"')
ls "file"
如您所见,ls $(echo '"file"')
扩展为 ls "file"
,这是上面 运行 成功执行的命令。现在,让我们尝试 运行 宁:
$ ls $(echo '"file"')
ls: cannot access '"file"': No such file or directory
如您所见,shell 不处理命令替换后保留的双引号。这是因为这些引号不被视为 shell-有效。因此,它们被视为普通字符并传递给 ls
,它抱怨目录中不存在名称以 "
开头和结尾的文件。
同样的情况也发生在这里:
$ cmd='ls "file"'
$ $cmd
ls: cannot access '"file"': No such file or directory
POSIX标准
Enclosing characters in single-quotes ( ' ' ) shall preserve the
literal value of each character within the single-quotes
换句话说,一旦双引号出现在单引号内,它就没有什么特殊的功能了:它只是另一个字符。
该标准还提到转义和双引号作为保留字符 "the literal value" 的方法。
实际后果
刚接触 shell 的人通常希望将命令存储在变量中,如上面的 cmd='ls "file"'
示例所示。但是,因为引号和其他 shell-active 字符一旦存储在变量中就不再是 shell active,复杂的情况总是失败。这就引出了一篇经典文章:
"I'm trying to put a command in a variable, but the complex cases always fail!"
POSIX shell 标准
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04
在第 2.6 节中说:
command substitution (...) shall be performed
(...)
Quote removal (...) shall always be performed last.
在我看来,在命令替换后没有执行引号删除:
$ echo "#"
#
$ echo '"'
"
符合预期,但是
$ echo $(echo '"')#"
>
我哪里不明白?
阅读后添加answer/comments:
按大家的说法,引号的考虑发生在解析的最开始,例如判断一个命令是否为偶数"acceptable"。那么为什么标准要强调,引用 删除 是在过程的后期执行的??
"then the outer command becomes
echo "#"
and is balanced"
那不是 'balanced' 因为第一个双引号 不算 。引号只有在命令行中不受阻碍地出现时才有意义。
为了验证,让我们看一下:
$ echo $(echo '"')#
"#
这是平衡的,因为 shell 确实认为 "
只是另一个字符。
相比之下,这是不平衡的,因为它只有一个 shell-active "
:
$ echo $(echo '"')#"
>
类似例子1
这里我们展示了同样的东西,但是使用了参数扩展而不是命令替换:
$ q='"'; echo $q
"
一旦 shell 将 "
替换为 $q
,人们可能会认为存在不平衡的双引号。但是,那个双引号是 参数扩展 的结果,因此 不是 一个 shell-active 引号。
类似例子2
让我们考虑一个包含 file
:
$ ls
file
$ ls "file"
file
正如您在上面看到的,引号删除是在 ls
为 运行 之前执行的。
但是,考虑这个命令:
$ echo ls $(echo '"file"')
ls "file"
如您所见,ls $(echo '"file"')
扩展为 ls "file"
,这是上面 运行 成功执行的命令。现在,让我们尝试 运行 宁:
$ ls $(echo '"file"')
ls: cannot access '"file"': No such file or directory
如您所见,shell 不处理命令替换后保留的双引号。这是因为这些引号不被视为 shell-有效。因此,它们被视为普通字符并传递给 ls
,它抱怨目录中不存在名称以 "
开头和结尾的文件。
同样的情况也发生在这里:
$ cmd='ls "file"'
$ $cmd
ls: cannot access '"file"': No such file or directory
POSIX标准
Enclosing characters in single-quotes ( ' ' ) shall preserve the literal value of each character within the single-quotes
换句话说,一旦双引号出现在单引号内,它就没有什么特殊的功能了:它只是另一个字符。
该标准还提到转义和双引号作为保留字符 "the literal value" 的方法。
实际后果
刚接触 shell 的人通常希望将命令存储在变量中,如上面的 cmd='ls "file"'
示例所示。但是,因为引号和其他 shell-active 字符一旦存储在变量中就不再是 shell active,复杂的情况总是失败。这就引出了一篇经典文章:
"I'm trying to put a command in a variable, but the complex cases always fail!"