รั้งการขยายตัวลึกลับใน Bash


19

นี้:

$ 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

เอกสารนี้อยู่ที่ไหนสักแห่ง? ทุบตีอ้างอิงไม่ได้พูดถึงมัน (แม้ว่ามันจะมีตัวอย่างการใช้มัน)

คำตอบ:


18

มันเป็นหนึ่งชั้นทีละชั้น:

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ตัวเองเอกสารที่ถูกขยายไป

สิ่งที่อาจเป็นเอกสารที่คุ้มค่าคือพวกเขาสามารถซ้อนกันได้ (อย่างแรก}ไม่ได้ปิดครั้งแรก{ในนั้น)

ฉันคิดว่าหอยสามารถเลือกที่จะแก้ไขปัญหาการจัดฟันด้านในก่อนเช่นโดยทำตามการปิดแต่ละครั้ง}:

  1. X{{a..c},{1..3}}
  2. 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)

  3. 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

  4. XaY X1Y XaY Xa2...

แต่ฉันไม่พบว่าโดยเฉพาะอย่างยิ่งง่ายขึ้นและไม่มีประโยชน์ (ดูตัวอย่างของเควินในความคิดเห็นเป็นต้น) มีจะยังคงมีความคลุมเครือบางอย่างเป็นลำดับที่ขยายจะต้องทำและนั่นไม่ใช่วิธีการcsh(เปลือกที่นำรั้ง การขยายตัวในช่วงปลายยุค 70 ในขณะที่{1..3}แบบฟอร์มมาภายหลัง (1995) จากzshและ{a..c}หลังจากนั้น (2004) จากbash) ก็ทำ

โปรดทราบว่าcsh(ตั้งแต่เริ่มต้นดูหน้า2BSD (1979) ) ทำเอกสารข้อเท็จจริงที่ว่าการขยายรั้งสามารถซ้อนกันได้แม้ว่าจะไม่ได้กล่าวอย่างชัดเจนว่าการขยายรั้งซ้อนซ้อนจะอย่างไร แต่คุณสามารถดูcshรหัสจากปี 1979เพื่อดูว่ามันทำไปแล้ว ดูว่ามันจัดการกับการทำรังอย่างชัดเจนได้อย่างไรและวิธีการแก้ไขนั้นเริ่มต้นจากการจัดฟันด้านนอก

ไม่ว่าในกรณีใดฉันไม่เห็นว่าการขยายตัวของ{a..c},{1..3}ตลับลูกปืนจะมีผลอย่างไร ในนั้น,ไม่มีตัวดำเนินการของการขยายรั้ง (เนื่องจากไม่ได้อยู่ในเครื่องหมายปีกกา) ดังนั้นจะถือว่าเหมือนอักขระทั่วไปใด ๆ


ดูเหมือนว่าแปลกสำหรับฉันว่าการจัดฟันด้านนอกควรได้รับการแก้ไขก่อนข้างใน
Hauke ​​Laging

@ stéphane-chazelas มีสองวิธีที่ชัดเจนว่าการแสดงออกนี้อาจจะแยกเป็น ทำไมถึงแยกวิเคราะห์ทางเดียวและไม่แยกกัน? ความคิดเห็นของคุณดูเหมือนจะไม่ให้คำอธิบาย
igal

ดังนั้นคำอธิบายนั้นสมเหตุสมผล แต่ถ้านี่เป็น "เอกสารที่ถูกขยายเป็น ... "มี URL หรือไม่?
xenoid

@xenoid ดูโซลูชันที่อัปเดตของฉัน
igal

1
@ (ทุกคน): /dev/{h,s}d{a..d}{1..4,}พิจารณาการขยายตัว ตอนนี้สมมติว่าคุณต้องการที่จะขยายมันยังรวมถึงและ/dev/null /dev/zeroหากการขยายตัวรั้งทำงานจากภายในสู่ภายนอกการขยายตัวนั้นจะน่ารำคาญจริงๆในการสร้าง แต่เนื่องจากการทำงานจากภายนอกเข้ามาจึงเป็นเรื่องที่ค่อนข้างง่าย:/dev/{null,zero,{h,s}d{a..d}{1..4,}}
เควิน

7

นี่คือคำตอบสั้น ๆ ในนิพจน์แรกจะใช้เครื่องหมายจุลภาคเป็นตัวคั่นดังนั้นการขยายรั้งจึงเป็นเพียงการเชื่อมต่อของนิพจน์ย่อยซ้อนกันสองตัว ในนิพจน์ที่สองคอมมาจะถือว่าเป็นนิพจน์ย่อยอักขระเดียวดังนั้นการแสดงออกของผลิตภัณฑ์จะเกิดขึ้น

สิ่งที่คุณพลาดไปคือนิยามของวิธีการใช้วงเล็บปีกกา นี่คือสามอ้างอิง:

คำอธิบายรายละเอียดเพิ่มเติมดังต่อไปนี้


คุณเปรียบเทียบผลลัพธ์ของนิพจน์นี้:

$ 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 Manualไม่ได้ให้คำจำกัดความที่สมบูรณ์

ฉันค้นหาเล็กน้อย แต่ไม่สามารถหาคำจำกัดความที่ขาดหายไป ดังนั้นฉันไปที่รหัสแหล่งที่มา:

แหล่งที่มาประกอบด้วยความเห็นที่เป็นประโยชน์สองสามข้อ อันดับแรกคือภาพรวมระดับสูงของอัลกอริธึมการขยายรั้ง:

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>

entry-point หลักของการขยายคือฟังก์ชันที่เรียกว่าbrace_expandซึ่งอธิบายไว้ดังนี้:

Return an array of strings; the brace expansion of TEXT.

ดังนั้นbrace_expandฟังก์ชั่นจะใช้สตริงที่แทนนิพจน์การขยายรั้งและส่งกลับอาร์เรย์ของสตริงที่ขยาย

เมื่อรวมการสังเกตสองข้อนี้เราจะเห็นว่าการขยายกำลังไปยังรายการของสตริงซึ่งแต่ละส่วนเชื่อมต่อกับคำนำ postamble นั้นจะถูกขยายเข้าไปในรายการของสตริงและแต่ละสตริงในรายการ postamble จะถูกรวมเข้ากับแต่ละสตริงในรายการ preamble / amble (เช่นผลิตภัณฑ์ของทั้งสองรายการจะเกิดขึ้น) แต่นี่ไม่ได้อธิบายถึงวิธีการประมวลผลและการ postamble โชคดีที่มีความคิดเห็นอธิบายว่าเป็นเช่นกัน amble ถูกประมวลผลโดยฟังก์ชันที่เรียกว่า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ฟังก์ชั่นนั้นเรียกว่าซ้ำใน postamble นี่เป็นคำอธิบายที่สมบูรณ์ของอัลกอริทึม

มีการอ้างอิงอื่น ๆ (ไม่เป็นทางการ) ที่ยืนยันการค้นพบนี้

สำหรับการอ้างอิงหนึ่งตรวจสอบทุบตีแฮกเกอร์วิกิพีเดีย ส่วนของการรวมและการซ้อนไม่ได้ช่วยแก้ไขปัญหาของคุณ แต่หน้าจะให้ไวยากรณ์ / ไวยากรณ์ของการขยายรั้งซึ่งฉันคิดว่าจะตอบคำถามของคุณ ไวยากรณ์ถูกกำหนดโดยรูปแบบต่อไปนี้:

{string1,string2,...,stringN}

{<START>..<END>}

<PREAMBLE>{........}

{........}<POSTSCRIPT>

<PREAMBLE>{........}<POSTSCRIPT>

และการแยกวิเคราะห์อธิบายไว้ดังนี้

การขยายของ Brace ใช้เพื่อสร้างสตริงโดยพลการ สตริงที่ระบุถูกใช้เพื่อสร้างชุดค่าผสมที่เป็นไปได้ทั้งหมดด้วย preambles และ postscripts ที่ล้อมรอบซึ่งเป็นทางเลือก

สำหรับการอ้างอิงอื่นดูที่คู่มือBash Beginner'sซึ่งมีดังต่อไปนี้เพื่อพูดว่า:

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}}

ในภาษาของ Wiki ของ Bash Hacker สิ่งนี้จะตรงกับรูปแบบแรก:

{string1,string2,...,stringN}

ที่ไหนN=2,string1={a..c}และstring2={1..3} - {<START>..<END>}ขยายรั้งภายในการดำเนินการครั้งแรกและแต่ละของพวกเขาเป็นอยู่ของแบบฟอร์ม อีกวิธีหนึ่งเราสามารถพูดได้ว่านี่เป็นนิพจน์การขยายแบบรั้งซึ่งประกอบด้วยเพียงความสูงส่ง amble เป็นรายการที่คั่นด้วยเครื่องหมายจุลภาคดังนั้นเราจึงผ่านรายการหนึ่งช่องในแต่ละครั้งและดำเนินการขยายเพิ่มเติมตามที่ต้องการ ไม่มีผลิตภัณฑ์ใดถูกสร้างขึ้นเนื่องจากไม่มีนิพจน์ที่อยู่ติดกัน (ใช้เครื่องหมายจุลภาคเป็นตัวคั่น)

ต่อไปให้ดูที่นิพจน์ที่สองของคุณ:

{a..c},{1..3}

ในภาษาของ Wiki ของ Bash Hacker นิพจน์นี้ตรงกับแบบฟอร์ม:

{........}<POSTSCRIPT>

ที่ลงท้ายเป็น ,{1..3}sub-แสดงออก อีกวิธีหนึ่งเราสามารถพูดได้ว่าการแสดงออกนี้มีความ{a..c}ถ่อมใจ ( ,{1..3}) และ postamble ( ) การเดินทอดน่องถูกขยายไปยังรายการa b cและจากนั้นเชื่อมโยงแต่ละรายการเหล่านี้เข้าด้วยกันกับแต่ละสายอักขระในการขยายตัวของ Postamble postamble มีการประมวลผลซ้ำ: มันมีคำนำของและเดินทอดน่องของ, นี้มีการขยายไปยังรายการ{1..3} ,1 ,2 ,3ทั้งสองรายการa b cและ,1 ,2 ,3a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3จะรวมกันแล้วในรูปแบบรายการสินค้า

มันอาจช่วยให้คำอธิบายเกี่ยวกับพีชคณิตแบบ psuedo - เกี่ยวกับวิธีการแยกวิเคราะห์นิพจน์เหล่านี้โดยที่วงเล็บ "[]" หมายถึงอาร์เรย์, "+" หมายถึงการเรียงลำดับของอาร์เรย์ที่เรียงกัน

นี่คือวิธีที่นิพจน์แรกถูกขยาย (หนึ่งขั้นต่อบรรทัด):

{{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

2

ความเข้าใจของฉันคือ:

เครื่องมือจัดฟันด้านในได้รับการแก้ไขก่อน (เช่นเคย) ซึ่งจะเปลี่ยน

{{a..c},{1..3}}

เข้าไป

{a,b,c,1,2,3}

เพราะว่า ,ภายในวงเล็บปีกกามันก็แยกองค์ประกอบรั้ง

แต่ในกรณีของ

{a..c},{1..3}

ที่,ไม่ได้อยู่ในวงเล็บปีกกานั่นคือมันเป็นตัวละครธรรมดาที่ทำให้เกิดการเปลี่ยนรูปรั้งทั้งสองด้าน


ดังนั้น{a..c}สามารถแก้ไขa,b,cหรือa b cขึ้นอยู่กับความชื้นและ Dow Jones ได้หรือไม่? เรียบร้อย
kubanczyk

ดูเหมือนว่าจะสับสนเล็กน้อย หาก{{a..c},{1..3}}เป็นเช่นเดียวกัน{a,b,c,1,2,3}แล้วไม่ควร{{a..c}.{1..3}}เหมือนกัน{a,b,c.1,2,3}หรือ แน่นอนว่านี่ไม่ใช่กรณี
ilkkachu

@ilkkachu ทำไมต้องเป็นเหมือนกัน ,เป็นอักขระการแยกการขยายรั้ง.ไม่ ทำไมอักขระธรรมดาควรนำไปสู่ผลลัพธ์เดียวกันเหมือนอักขระพิเศษหรือไม่ c.1เป็นองค์ประกอบรั้ง แต่ในเป็นสมอสำหรับการขยายรั้งด้านซ้ายและด้านขวา ด้วยวงเล็บปีกกาด้านนอกจะใช้สำหรับการขยายรั้งเพราะเนื้อหาของพวกเขามีรูปแบบการขยายรั้งที่พวกเขาไม่ได้เพราะเนื้อหาของพวกเขาไม่ได้มีรูปแบบที่ {a..c}.{1..3}.,.
Hauke ​​Laging

@HaukeLaging ดีถ้า{{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}
ilkkachu

@kubanczyk คุณไม่ควรสนุกกับคำตอบที่คุณไม่เข้าใจ
Hauke ​​Laging
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.