我正在使用GNU bash
4.3.48
. 考虑以下两个仅相差一个美元符号的命令。
命令 1:
echo "(echo " * ")"
命令 2:
echo "$(echo " * ")"
它们的输出分别是
(echo test.txt ppcg.sh )
和
*
所以很明显在第一种情况下*
是全局的,这意味着第一个引号与第二个引号形成一对,第三个和第四个形成另一个对。
在第二种情况下,*
不是全局的,并且输出中正好有两个额外的空格,一个在星号之前,一个在星号之后,这意味着第二个引号与第三个引号一起出现,而第一个引号与第四个引号一起出现。
$()
除了引号不与下一个匹配而是嵌套的构造之外,还有其他情况吗?这种行为是否有据可查,如果有,我在哪里可以找到相应的文档?
任何可以在字符串中插入的嵌套结构都可以在其中包含更多的字符串:它们像新脚本一样被解析,直到结束标记,甚至可以嵌套多层深度。所有这些都以 . 开头
$
。所有这些都记录在 Bash 手册和 POSIX shell 命令语言规范的组合中。这些结构有几种情况:
$( ... )
如您所见,用 替换命令。POSIX 指定此行为:引号是有效 shell 脚本的一部分,因此它们的正常含义是允许的。
`
。高级参数替换实例的“word”元素,例如
${parameter:-word}
. “词”的定义是:- 包括引用的文本甚至混合引号
a"b"c'd'e
- 尽管扩展的实际行为比这更自由一些,例如${x:-hello world}
也有效。用 进行算术扩展,
$(( ... ))
虽然它在那里基本上没用(但您也可以嵌套命令替换或变量扩展,然后在其中有用地使用引号)。POSIX 声明:所以这种行为是明确要求的。这意味着
echo "abc $((4 "*" 5))"
算术,而不是通配符。请注意,旧式
$[ ... ]
算术扩展的处理方式不同:如果出现引号,则无论扩展是否被引用,引号都是错误的。这个表格根本没有记录,无论如何也不打算使用。$"..."
,它实际上将"
用作核心元素。$"
被视为一个单元。还有一种您可能没想到的嵌套情况,不涉及引号,它带有大括号扩展:
{a,b{c,d},e}
扩展为“a bc bd e”。但是,不${x:-a{b,c}d}
嵌套;它被视为参数替换,给出“ ”,后跟“ ”。这也记录在案:a{b,c
d}
作为一般规则,所有定界构造都独立于周围的上下文解析它们的主体(并且异常被视为错误)。本质上,在看到命令替换代码时,它只是要求解析器从主体中消耗它可以使用的东西,就好像它是一个新程序一样,然后在子解析器运行时
$(
检查预期的终止标记(未转义的)
or )))
是否出现}
它可以消耗的东西。如果您考虑递归下降解析器的功能,那只是对基本情况的简单递归。一旦你有了字符串插值,它实际上比其他方式更容易做到。不管底层的解析技术如何,支持这些结构的 shell 都会给出相同的结果。
您可以通过这些结构尽可能深入地嵌套引用,它会按预期工作。看到中间的引用不会让任何地方感到困惑;相反,这将是内部上下文中新引用字符串的开始。
printf
也许用(而不是)查看两个示例echo
会有所帮助:它打印
(echo
(第一个词,包括尾随空格)、一些文件和结束词)
。括号只是引用字符串的一部分
(echo
。星号(现在不加引号,因为两个双引号是配对的)作为一个 glob 扩展为匹配文件的列表。
然后是右括号。
但是,您的第二个命令的工作方式如下:
$
开始命令替换。这开始了一个新的报价。星号是带引号的
" * "
,这就是命令(这里是命令而不是带引号的字符串)echo
输出的内容。最后,printf
重新格式化*
并将其打印为< * >
.