Answers:
好吧,一次解开一层:
X{{a..c},{1..3}}Y
记录为被扩大到X{a..c}Y
X{1..3}Y
(这是X{A,B}Y
扩大到XA
XB
与A
被{a..c}
并B
为{1..3}
),自己记录为被扩大到XaY
XbY
XcY
X1Y
X2Y
X3Y
。
可能值得记录的是它们可以嵌套(例如,第一个}
不会在其中关闭第一个{
)。
我想炮弹可能会选择先解析内部括号,例如}
依次对每个闭合进行操作:
X{{a..c},{1..3}}
X{a,{1..3}}Y
X{b,{1..3}}Y
X{c,{1..3}}Y
(即A{a..c}B
扩大到AaB
AbB
AcB
,这里A
是X{
与B
是,{1..3}Y
)
X{a,1}Y
X{a,2}Y
X{a,3}Y
X{b,1}Y
X{b,2}Y
X{b,3}Y
X{c,1}Y
X{c,2}Y
X{c,3}Y
XaY
X1Y
XaY
Xa2
...但是我发现它不是特别直观和有用(例如,请参见Kevin的示例中的注释),关于扩展的执行顺序仍然存在一些歧义,而事实并非如此csh
(引入了brace的shell)扩展在70年代末,而{1..3}
从形态后(1995年)来zsh
,并{a..c}
还从后(2004年)bash
)做到了。
请注意csh
(从一开始,请参见2BSD(1979)手册页)确实记录了括号扩展可以嵌套的事实,尽管并未明确说明嵌套的括号扩展将如何扩展。但是您可以查看csh
1979年的代码,看看当时是如何完成的。了解它如何确实显式地处理嵌套,以及如何从外部大括号开始解析它。
无论如何,我并没有真正看到的扩展{a..c},{1..3}
有什么影响。在这里,,
并不是括号扩展的运算符(因为它不在括号内),因此被视为任何普通字符。
/dev/{h,s}d{a..d}{1..4,}
。现在假设您想将其扩展为还包括/dev/null
和/dev/zero
。如果括号内的扩展从内到外起作用,那么构建该扩展确实很烦人。但是因为它是从外部开始工作的,所以非常琐碎:/dev/{null,zero,{h,s}d{a..d}{1..4,}}
这是简短的答案。在第一个表达式中,逗号用作分隔符,因此括号扩展只是两个嵌套子表达式的串联。在第二个表达式逗号本身视为单个字符的子表达式,所以产物的表达被形成。
您所缺少的是括号扩展如何执行的定义。这是三个参考:
以下是更详细的说明。
您比较了此表达式的结果:
$ echo {{a..c},{1..3}}
a b c 1 2 3
该表达式的结果:
$ echo {a..c},{1..3}
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
您说这很难解释,即这是违反直觉的。缺少的是括号处理方式的正式定义。您注意到,《Bash手册》没有给出完整的定义。
我进行了一些搜索,但也找不到丢失的(完整的,正式的)定义。所以我去了源代码:
来源包含一些有用的注释。首先是大括号扩展算法的高级概述:
Basic idea: Segregate the text into 3 sections: preamble (stuff before an open brace), postamble (stuff after the matching close brace) and amble (stuff after preamble, and before postamble). Expand amble, and then tack on the expansions to preamble. Expand postamble, and tack on the expansions to the result so far.
因此,大括号扩展令牌的格式如下:
<PREAMBLE><AMBLE><POSTAMBLE>
扩展的主要入口点是一个称为的函数brace_expand
,其描述如下:
Return an array of strings; the brace expansion of TEXT.
因此,该brace_expand
函数采用表示括号扩展表达式的字符串,并返回扩展字符串数组。
结合这两个观察结果,我们可以看到amble扩展为一个字符串列表,每个字符串都串联在preamble上。然后将后同步码扩展为字符串列表,并将后同步码列表中的每个字符串连接到前同步码/同步码列表中的每个字符串上(即,形成两个列表的乘积)。但这并未说明如何处理amble和postamble。幸运的是,还有一条评论对此进行了描述。序言由称为expand_amble
的函数处理,该函数的定义前面带有以下注释:
Expand the text found inside of braces. We simply try to split the text at BRACE_ARG_SEPARATORs into separate strings. We then brace expand each slot which needs it, until there are no more slots which need it.
在代码的其他地方,我们看到BRACE_ARG_SEPARATOR被定义为逗号。这清楚地表明,同步码是逗号分隔的字符串列表,其中某些字符串也可能是括号扩展表达式。这些字符串然后形成单个数组。最后,我们还可以看到after expand_amble
被调用,brace_expand
然后在后同步码上递归调用该函数。这为我们提供了对该算法的完整描述。
还有其他一些(非正式的)参考文献也证实了这一发现。
作为参考,请查看Bash Hackers Wiki。关于合并和嵌套的部分并未完全解决您的问题,但该页面确实提供了括号扩展的语法/语法,我认为这确实回答了您的问题。语法由以下模式给出:
{string1,string2,...,stringN}
{<START>..<END>}
<PREAMBLE>{........}
{........}<POSTSCRIPT>
<PREAMBLE>{........}<POSTSCRIPT>
解析描述如下:
括号扩展用于生成任意字符串。指定的字符串用于生成所有可能的组合以及周围可选的前导和后记。
作为其他参考,请参阅《Bash入门指南》,其中包含以下内容:
Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace-expanded take the form of an optional PREAMBLE, followed by a series of comma-separated strings between a pair of braces, followed by an optional POSTSCRIPT. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.
因此,要解析括号扩展表达式,我们从左到右,扩展每个表达式并形成连续的乘积(关于字符串串联操作)。
现在,让我们考虑您的第一个表达式:
{{a..c},{1..3}}
在Bash Hacker's Wiki的语言中,这与第一种形式匹配:
{string1,string2,...,stringN}
其中N=2
,string1={a..c}
和string2={1..3}
-首先被执行内部括号扩展和形式的它们中的每{<START>..<END>}
。或者,我们可以说这是一个括号扩展表达式,它仅包含一个序言(没有前导或后序)。序言是用逗号分隔的列表,因此我们一次遍历该列表的一个插槽,并在需要时执行其他扩展。因为没有相邻的表达式(逗号用作分隔符),所以没有形成乘积。
接下来,让我们看看您的第二个表达式:
{a..c},{1..3}
在Bash Hacker's Wiki的语言中,此表达式与以下形式匹配:
{........}<POSTSCRIPT>
后记是子表达式,{1..3}
。或者,我们可以说此表达式具有一个amble({a..c}
)和一个postamble(,{1..3}
)。将该扩展词扩展到列表a b c
,然后在扩展后同步码中将每个字符串与每个字符串连接在一起。后序码是递归处理的:它的前导码为,
,而后序码为{1..3}
。这将扩展到列表,1 ,2 ,3
。两个列表a b c
和,1 ,2 ,3
再结合形成的产品列表a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
。
对如何解析这些表达式进行伪代数描述可能会有所帮助,其中括号“ []”表示数组,“ +”表示数组串联,“ *”表示笛卡尔积(相对于串联)。
这是第一个表达式的扩展方式(每行一步):
{{a..c},{1..3}}
{a..c} + {1..3}
[a b c] + [1 2 3]
a b c 1 2 3
这是第二个表达式的扩展方式:
{a..c},{1..3}
{a..c} * ,{1..3}
[a b c] * [,1 ,2 ,3]
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
我的理解是:
首先解决内部括号的问题(一如既往)
{{a..c},{1..3}}
进入
{a,b,c,1,2,3}
由于,
中括号内的,因此仅分隔了括号元素。
但是在
{a..c},{1..3}
在,
没有大括号内,即它是造成双方支柱排列一个普通的字符。
{a..c}
是解决a,b,c
还是a b c
取决于湿度和道琼斯指数?整齐。
{{a..c},{1..3}}
与相同{a,b,c,1,2,3}
,那么不{{a..c}.{1..3}}
应该与相同{a,b,c.1,2,3}
吗?当然不是这样。
,
是大括号扩展分隔符,.
不是。为什么普通字符会导致与特殊字符相同的结果?c.1
是支撑元素。但是,在{a..c}.{1..3}
该.
是左右括号扩展的锚点。随着,
外括号,括号扩展,因为它们的内容有括号扩展格式,.
它们不是因为它们的内容不具有这种格式。
{{a..c},{1..3}}
转成{a,b,c,1,2,3}
那么一些逗号刚刚出现之间a
,b
和c
。为什么它们不以相同的方式出现{a..c}.{1..3}
?@kubanczyk的评论是关于同一件事的,如果逗号像这样出现,我们如何知道扩展何时生成逗号,何时不生成逗号?答案是,它永远不会自己产生任何逗号,它会生成单词列表。所以什么都没有变成{a,b,c,1,2,3}
或{a,b,c.1,2,3}
。