Đây là câu trả lời ngắn. Trong biểu thức đầu tiên, dấu phẩy được sử dụng như một dấu phân cách, vì vậy việc mở rộng dấu ngoặc chỉ là sự kết hợp của hai biểu thức con lồng nhau. Trong biểu thức thứ hai, dấu phẩy được coi là biểu thức con một ký tự, do đó các biểu thức sản phẩm được hình thành.
Những gì bạn đã thiếu là định nghĩa về cách thực hiện mở rộng cú đúp. Dưới đây là ba tài liệu tham khảo:
Một lời giải thích chi tiết hơn sau đây.
Bạn đã so sánh kết quả của biểu thức này:
$ echo {{a..c},{1..3}}
a b c 1 2 3
kết quả của biểu thức này:
$ echo {a..c},{1..3}
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
Bạn nói rằng điều này thật khó để giải thích, tức là điều này là phản trực giác. Điều còn thiếu là một định nghĩa chính thức về cách mở rộng niềng răng được xử lý. Bạn lưu ý rằng Hướng dẫn Bash không đưa ra định nghĩa đầy đủ.
Tôi đã tìm kiếm một chút nhưng tôi cũng không thể tìm thấy định nghĩa còn thiếu (hoàn chỉnh, chính thức). Vì vậy, tôi đã đi đến mã nguồn:
Nguồn chứa một vài ý kiến hữu ích. Đầu tiên là tổng quan cấp cao về thuật toán mở rộng niềng răng:
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.
Vì vậy, định dạng của mã thông báo mở rộng cú đúp là như sau:
<PREAMBLE><AMBLE><POSTAMBLE>
Điểm vào chính để mở rộng là một hàm được gọi brace_expand
là mô tả như sau:
Return an array of strings; the brace expansion of TEXT.
Vì vậy, brace_expand
hàm lấy một chuỗi biểu thị một biểu thức mở rộng dấu ngoặc và trả về mảng các chuỗi được mở rộng.
Kết hợp hai quan sát này, chúng ta thấy rằng amble được mở rộng thành một danh sách các chuỗi, mỗi chuỗi được nối vào phần mở đầu. Sau đó, postamble được mở rộng thành một danh sách các chuỗi và mỗi chuỗi trong danh sách postamble được nối vào từng chuỗi trong danh sách mở đầu / amble (nghĩa là sản phẩm của hai danh sách được tạo thành). Nhưng điều này không mô tả cách xử lý amble và postamble. May mắn thay có một bình luận mô tả đó là tốt. Amble được xử lý bởi một hàm gọi là expand_amble
định nghĩa được đi trước bởi nhận xét sau:
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.
Ở những nơi khác trong mã chúng ta thấy rằng BRACE_ARG_SEPARATOR được xác định là dấu phẩy. Điều này cho thấy rõ rằng amble là một danh sách các chuỗi được phân tách bằng dấu phẩy, một số trong đó cũng có thể là biểu thức mở rộng dấu ngoặc. Các chuỗi này sau đó tạo thành một mảng duy nhất. Cuối cùng, chúng ta cũng có thể thấy rằng sau khi expand_amble
được gọi là brace_expand
hàm sau đó được gọi đệ quy trên postamble. Điều này cung cấp cho chúng tôi một mô tả đầy đủ của thuật toán.
Có một số tài liệu tham khảo (không chính thức) khác chứng thực phát hiện này.
Để tham khảo, hãy xem Bash Hackers Wiki . Phần kết hợp và lồng nhau không giải quyết được vấn đề của bạn, nhưng trang này đưa ra cú pháp / ngữ pháp của việc mở rộng dấu ngoặc, mà tôi nghĩ là đã trả lời câu hỏi của bạn. Cú pháp được đưa ra bởi các mẫu sau:
{string1,string2,...,stringN}
{<START>..<END>}
<PREAMBLE>{........}
{........}<POSTSCRIPT>
<PREAMBLE>{........}<POSTSCRIPT>
Và phân tích cú pháp được mô tả như sau:
Mở rộng cú đúp được sử dụng để tạo các chuỗi tùy ý. Các chuỗi được chỉ định được sử dụng để tạo ra tất cả các kết hợp có thể với các phần mở đầu và phần mô tả xung quanh tùy chọn.
Để tham khảo, hãy xem Hướng dẫn dành cho người mới bắt đầu của Bash , có đoạn sau:
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.
Vì vậy, để phân tích các biểu thức mở rộng dấu ngoặc, chúng ta đi từ trái sang phải, mở rộng từng biểu thức và tạo thành các sản phẩm kế tiếp nhau (liên quan đến hoạt động nối chuỗi).
Bây giờ hãy xem xét biểu hiện đầu tiên của bạn:
{{a..c},{1..3}}
Trong ngôn ngữ của Bash Hack Bash Wiki, mẫu này khớp với mẫu đầu tiên:
{string1,string2,...,stringN}
Trường hợp N=2
, string1={a..c}
và string2={1..3}
- mở rộng niềng răng bên trong được thực hiện đầu tiên và mỗi trong số chúng đều có dạng {<START>..<END>}
. Ngoài ra, chúng ta có thể nói rằng đây là một biểu thức mở rộng cú đúp chỉ bao gồm một amble (không có phần mở đầu hoặc postamble). Amble là một danh sách được phân tách bằng dấu phẩy, vì vậy chúng tôi đi qua danh sách một vị trí tại một thời điểm và thực hiện các mở rộng bổ sung khi cần thiết. Không có sản phẩm nào được hình thành vì không có biểu thức liền kề (dấu phẩy được sử dụng làm dấu phân cách).
Tiếp theo hãy nhìn vào biểu thức thứ hai của bạn:
{a..c},{1..3}
Trong ngôn ngữ của Bash Hacker Wiki, biểu thức này khớp với biểu mẫu:
{........}<POSTSCRIPT>
trong đó phần tái bút là biểu thức con ,{1..3}
. Ngoài ra, chúng ta có thể nói rằng biểu thức này có amble ( {a..c}
) và postamble ( ,{1..3}
). Amble được mở rộng vào danh sách a b c
và sau đó mỗi chuỗi này được nối với từng chuỗi trong quá trình mở rộng của postamble. Postamble được xử lý đệ quy: nó có phần mở đầu ,
và khả năng {1..3}
. Điều này được mở rộng vào danh sách ,1 ,2 ,3
. Hai danh sách a b c
và ,1 ,2 ,3
sau đó được kết hợp để tạo thành danh sách sản phẩm a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
.
Nó có thể giúp đưa ra một mô tả đại số psuedo về cách các biểu thức này được phân tích cú pháp, trong đó dấu ngoặc "[]" biểu thị mảng, "+" biểu thị phép nối mảng và "*" biểu thị sản phẩm của Cartesian (đối với phép nối).
Đây là cách biểu thức đầu tiên được mở rộng (một bước trên mỗi dòng):
{{a..c},{1..3}}
{a..c} + {1..3}
[a b c] + [1 2 3]
a b c 1 2 3
Và đây là cách biểu thức thứ hai được mở rộng:
{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