bash中的双引号如何匹配(配对)?


15

我正在使用GNU bash 4.3.48。考虑以下两个仅相差一个美元符号的命令。

指令1:

echo "(echo " * ")"

指令2:

echo "$(echo " * ")"

它们的输出分别是

(echo  test.txt ppcg.sh )

 * 

因此很明显,在第一种情况下,*会出现问题,这意味着第一个引号与第二个引号形成一个对,而第三个和第四个引号形成另一个对。

在第二种情况下,*不会被混淆,并且输出中恰好有两个额外的空格,一个在星号之前,另一个在空格之后,这意味着第二个引号与第三个引号一起使用,第一个引号与第四个引号一起使用。

除了$()引号不与下一个引号匹配,而是嵌套的引号之外,还有其他情况吗?此行为是否已得到充分记录?如果是,我在哪里可以找到相应的文件?



UL SE中的语法高亮再次是错误和误导的,尽管美化JS是应归咎于这一点。

Answers:


14

可以在字符串内插的任何嵌套构造都可以在其中包含其他字符串:它们像新脚本一样被解析,直到结束标记为止,甚至可以嵌套多层。所有这些酒吧之一都以开头$。所有这些都在Bash手册和POSIX Shell命令语言规范的结合中进行了说明。

这些构造有几种情况:

  • 找到的命令替换$( ... )为。POSIX指定此行为

    使用该$(command)格式,在开括号后面到匹配的闭括号后面的所有字符构成命令。任何有效的Shell脚本均可用于命令 ...

    引号是有效的Shell脚本的一部分,因此可以使用其正常含义。

  • `也使用命令替换。
  • 高级参数替换实例(例如)${parameter:-word}的“ 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}”。这也有记载

使用花括号时,匹配的结尾花括号是第一个'}',不会被反斜杠或加引号的字符串引起,也不会位于嵌入式算术扩展,命令替换或参数扩展之内。


通常,所有带分隔符的构造都独立于周围的上下文来解析其主体(并且异常被视为bug)。从本质上说,在看到$(命令替换的代码只是要求解析器从身体消耗所能,就好像它是一个新的程序,然后检查,预期结束标志(一个转义)))})出现一次子解析器运行消耗掉的东西。

如果您考虑递归下降解析器的功能,那只是对基本情况的简单递归。一旦您完成了字符串插值,实际上比其他方法更容易实现。不管底层的解析技术如何,支持这些构造的shell都会产生相同的结果。

通过这些结构,您可以根据需要嵌套最深的引用,并且它将按预期工作。在中间看到报价,不会让任何地方感到困惑。相反,这将是内部上下文中新的带引号的字符串的开始。


谢谢。在中"blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")",为什么第二个双引号不是第一个双引号的结尾(如您的回复中突出显示的语法所示),而是内部字符串的开头$(...)?是因为bash的解析器是自上而下而不是自下而上的吗?
所有

2
的处理方式有很多变化"${var-"foo"}"echo "${-+"*"}"例如echo *,与Bourne或Korn外壳中的处理方式相同),并且该行为将在下一版标准中明确规定。另请参见在讨论mail-archive.com/austin-group-l@opengroup.org/msg00167.html
斯特凡Chazelas

3

也许用printf(而不是echo)查看两个示例会有所帮助:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

它打印(echo (第一个单词,包括一个尾随空格),一些文件和结尾单词)
括号只是加引号的字符串的一部分(echo 
星号(由于两个双引号已成对,所以现在不加引号)被扩展为一个匹配文件列表。
然后,右括号。

但是,第二条命令的工作方式如下:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

$开始命令替换。从头开始重新报价。
星号加引号" * ",这就是命令(这里是命令而不是带引号的字符串)echo输出的内容。最后,printf重新格式化*并将其打印为< * >

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.