Вот краткий ответ. В первом выражении запятая используется в качестве разделителя, поэтому расширение в скобках является просто объединением двух вложенных подвыражений. Во втором выражении запятая сам по себе рассматривается как односимвольный подвыражению, поэтому выражения продукта будут сформированы.
Чего вам не хватало, так это определения того, как выполняются скобки-расширения. Вот три ссылки:
Более подробное объяснение приведено ниже.
Вы сравнили результат этого выражения:
$ 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 расширено до списка строк, каждая из которых объединена в преамбуле. Затем метка раскладывается в список строк, и каждая строка в списке меток объединяется с каждой строкой в списке преамбул / амблей (т.е. формируется произведение двух списков). Но это не описывает, как обрабатываются 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 определяется как запятая. Это проясняет, что amble - это список строк, разделенных запятыми, некоторые из которых также могут быть выражениями в скобках. Эти строки затем образуют один массив. Наконец, мы можем видеть , что после того, как 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 это соответствует первой форме:
{string1,string2,...,stringN}
Там , где N=2
, string1={a..c}
и string2={1..3}
- внутренние скобки разложения выполняется первым , и каждый из них формы {<START>..<END>}
. В качестве альтернативы, мы можем сказать, что это выражение расширения скобки, которое состоит только из amble (без преамбулы или постамбла). Amble - это список, разделенный запятыми, поэтому мы просматриваем список по одному слоту за раз и выполняем дополнительные расширения, где это необходимо. Продукт не формируется, потому что нет смежных выражений (запятая используется в качестве разделителя).
Теперь давайте посмотрим на ваше второе выражение:
{a..c},{1..3}
На языке вики Bash Hacker's это выражение соответствует форме:
{........}<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