Ось коротка відповідь. У першому виразі кома використовується як роздільник, тому розширення дужок - це лише з'єднання двох вкладених підвиражень. У другому вираженні кома сам по собі розглядається як Односимвольний подвираженія, тому вираження продукту будуть сформовані.
Те, що вам не вистачало, було визначення того, як виконуються розширення дужок. Ось три посилання:
Далі йде більш детальне пояснення.
Ви порівняли результат цього виразу:
$ 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
функція приймає рядок, що представляє вираз розширення дужок і повертає масив розширених рядків.
Поєднуючи ці два спостереження, ми бачимо, що амблі розгорнуто до списку рядків, кожна з яких з'єднана в преамбулу. Потім перекладний елемент розширюється на список рядків, і кожна рядок у списку постамбелів об'єднується в кожну рядок у списку преамбули / амблі (тобто утворюється добуток двох списків). Але це не описує, як обробляються амблі та поштовби. На щастя, є коментар, який також описує це. Амбл обробляється функцією під назвою 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 . Розділ про поєднання та вкладення не зовсім вирішує вашу проблему, але на цій сторінці подано синтаксис / граматику розширення дужок, що, на мою думку, відповідає на ваше запитання. Синтаксис задається такими шаблонами:
{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}}
Мовою Віки Баша Хакера це відповідає першій формі:
{string1,string2,...,stringN}
Там , де N=2
, string1={a..c}
і string2={1..3}
- внутрішні дужки розкладання виконується першим , і кожен з них форми {<START>..<END>}
. Як варіант, ми можемо сказати, що це вираження розширення дужок, яке складається лише з амбла (без преамбули та постамбули). Амбл - це список, розділений комами, тому ми проходимо по одному слоту за раз і виконуємо додаткові розширення, де потрібно. Жоден твір не утворюється, оскільки немає суміжних виразів (кома використовується як роздільник).
Далі розглянемо ваш другий вираз:
{a..c},{1..3}
Мовою Віки Баша Хакера цей вираз відповідає формі:
{........}<POSTSCRIPT>
де постскрипт - це підвираз ,{1..3}
. Як варіант, ми можемо сказати, що цей вираз має {a..c}
амблі ( ( ) і постамбл ( ,{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