ทำไมกลุ่มคำสั่ง brace ต้องการช่องว่างหลังจากเปิดวงเล็บปีกกาใน POSIX Shell Grammar


10

TL; DR : ทำไมช่องว่างกลุ่ม POSIX รั้งจำเป็นหลังจาก{คำสงวน แต่ subshell ไม่หลังจากคำสงวน(?

ไวยากรณ์ของ POSIX เชลล์กำหนดกลุ่มวงเล็บปีกกาและ subshell ดังนี้

brace_group      : Lbrace compound_list Rbrace

subshell         : '(' compound_list ')'

ทีนี้ถ้าเราอ่านว่าช่องว่างนั้นสำคัญมาก นี่หมายความว่าจะต้องมีพื้นที่ที่แยกเปิดและปิดวงเล็บปีกกาและวงเล็บเช่นเดียวกับใน

{ echo hello world; }

( echo hello world )

สิ่งนี้จะสอดคล้องกับคำสั่งคำสั่งผสม :

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

อย่างไรก็ตามสิ่งที่ไม่สมเหตุสมผลคือเหตุผล(list)และ( list )ทำงานได้ดี ( (ไม่จำเป็นต้องใช้พื้นที่นั้นหลังจากนั้น) อย่างไรก็ตามการขยายตัวของรั้งต้องมีพื้นที่ชั้นนำเช่น{echo hello;}ไม่ทำงาน

แน่นอนว่าคำที่สงวนไว้ซึ่งได้รับการปฏิบัติเหมือนคำว่าเชลล์จำเป็นต้องเว้นวรรคเพื่อให้สอดคล้องกับแนวคิดของการแยกฟิลด์อย่างไรก็ตามคำจำกัดความของตัวเองไม่ได้พูดถึงช่องว่าง นอกจากนี้หาก{และ(ทั้งคู่ถือว่าคำที่สงวนไว้โดยนิยาม POSIX ของคำสั่งผสมทำไมพวกเขาถึงได้รับการปฏิบัติที่แตกต่างกันเกี่ยวกับอักขระช่องว่างหลังจากคำที่สงวนไว้เหล่านี้? ตอนนี้คู่มือksh (1)ไม่ระบุสถานะ:

คำซึ่งเป็นลำดับของอักขระถูกคั่นด้วยอักขระช่องว่างสีขาว (ช่องว่างแท็บและขึ้นบรรทัดใหม่) หรืออักขระเมตา (<,>, |,;, และ (และ))

กล่าวอีกนัยหนึ่งมันสมเหตุสมผลแล้วที่ ksh จะจำได้(ว่าเป็นตัวคั่นคำโดยที่คำแรกจะเป็นคำสั่งหรือการกำหนดตัวแปร POSIX แต่ดูเหมือนจะไม่พูดถึง(ว่าเป็นตัวละครเมตา คำอธิบายที่เป็นไปได้เพียงอย่างเดียวที่ฉันพบเท่าที่ POSIX ไวยกรณ์ไป{คือถือเป็น "โทเค็น" โดยที่(ไม่อยู่ในรายการเดียว

/* These are reserved words, not operator tokens, and are
   recognized when reserved words are recognized. */


%token  Lbrace    Rbrace    Bang
/*      '{'       '}'       '!'   */

ดังนั้นอะไรคือเหตุผลที่แม่นยำสำหรับความคลาดเคลื่อนนี้

หมายเหตุคำตอบที่ยอมรับได้:

  • ย้ายเครื่องหมายถูกที่ตอบรับไปยังคำตอบของไอแซคเนื่องจากมีรูปแบบมาตรฐานซึ่งตรงกับคำถามของฉัน:

    ตัวอย่างเช่น '(' และ ')' เป็นตัวดำเนินการควบคุมดังนั้นจึงไม่<space>จำเป็นใน (รายการ) อย่างไรก็ตาม '{' และ '}' เป็นคำที่สงวนไว้ใน {list;} ดังนั้นในกรณีนี้การนำหน้า<space>และ<semicolon>จำเป็น

  • ยอมรับคำตอบของ Kusalananda คำตอบของ Kusalananda กล่าวถึงสิ่งที่ฉันต้องการ แต่ส่วนใหญ่มาจากมุมมองที่ไม่เป็นทางการและเป็นธรรมชาติ มันชี้ให้เห็น{ว่าเป็นคำสงวนและ(เป็นผู้ประกอบการ ไมเคิลโฮเมอร์ก็สังเกตเห็นเหมือนกันในความคิดเห็น - ที่คำจำกัดความคำสั่งผสม (เน้นเพิ่ม):

    คำสั่งรวมเหล่านี้แต่ละคำมีคำที่สงวนไว้หรือผู้ดำเนินการควบคุมที่จุดเริ่มต้น

  • {ถูกกำหนดให้เป็นคำสงวนคล้ายกับforหรือwhileอยู่ในรายการในไวยากรณ์ของเชลล์ (ดูบล็อกรหัสสุดท้ายในคำถาม)

  • มาตรา 2.9 ฯ (เน้นเพิ่ม):

    โดยเฉพาะการรับรองรวมถึงการเว้นวรรคระหว่างโทเค็นในบางสถานที่ที่<blank>ไม่จำเป็นต้องใช้ s (เมื่อโทเค็นตัวใดตัวหนึ่งเป็นตัวดำเนินการ)

  • ในขณะที่มาตรฐานไม่ได้กำหนดอย่างชัดเจน(ว่าเป็นโอเปอเรเตอร์(จะถูกอ้างถึงเป็นโอเปอเรเตอร์ โดยเฉพาะอย่างยิ่งส่วน 2.9.2พูดว่า

    หากท่อเริ่มต้นด้วยคำสงวน! และ command1 เป็นคำสั่ง subshell แอปพลิเคชั่นจะต้องแน่ใจว่า (ตัวดำเนินการที่จุดเริ่มต้นของ command1 ถูกแยกออกจาก! ด้วยอักขระหนึ่งตัวหรือมากกว่าพฤติกรรมของคำที่สงวนไว้! ตามด้วยตัวดำเนินการทันที

  • คำถามเกี่ยวกับ Stack Overflowโดย Digital Trauma ชี้ให้เห็นส่วนที่ 2.4 ในคำสงวน:

    การรับรู้นี้จะเกิดขึ้นก็ต่อเมื่อไม่มีการอ้างถึงตัวละครและเมื่อใช้คำว่า:

    - คำแรกของคำสั่ง

  • ดังที่กล่าวไว้ในคำตอบของ Kusalananda "ช่องว่างที่แสดงในไวยากรณ์ POSIX ไม่ใช่ช่องว่างที่จำเป็นต้องมีในข้อมูลอินพุตเชลล์ แต่เป็นเพียงวิธีแสดงไวยากรณ์เองมันเป็นความจริงที่วงเล็บปีกกาเป็นคำสงวนที่มีความหมายว่า พวกเขาจะต้องถูกล้อมรอบด้วยช่องว่าง "ตามที่กล่าวไว้โดยไมเคิลโฮเมอร์ในความคิดเห็น:" ถ้าช่องว่างมีความสำคัญในสิทธิของตนเองพวกเขาจะต้องระบุไว้ในการผลิต "

ปิดคดี.


3
หากช่องว่างมีความสำคัญในสิทธิของตนเองพวกเขาจำเป็นต้องแสดงรายการในการผลิต
Michael Homer

2
"นอกจากนี้หาก{และ(ทั้งคู่ถือว่าคำที่สงวนไว้โดยนิยาม POSIX ของคำสั่งผสม" cf msgstr "คำสั่งผสมเหล่านี้แต่ละคำมีคำสงวนหรือโอเปอเรเตอร์ควบคุมที่จุดเริ่มต้น".
Michael Homer

2
@SergiyKolodyazhnyy ฉันเชื่อว่าเขาหมายความว่าหากพื้นที่มีความสำคัญไวยากรณ์จะต้องมีอักขระช่องว่างที่ชัดเจน ( ' ') แต่ช่องว่างจะถูกบอกเป็นนัย ๆ ว่าโทเค็นคืออะไร
Kusalananda

2
ข้อกำหนดสเปคของคลาสโทเค็นคือ ... งุ่มง่ามที่จะพูดอย่างน้อย ไวยากรณ์ทั้งหมดนั้นแย่มากและสเป็คจะกำหนดสิ่งต่าง ๆ เป็นร้อยแก้วในข้อความ (บางครั้งโดยปริยาย!) ในกฎร้อยแก้วก่อนหน้าไวยากรณ์และไวยากรณ์เอง มันยากที่จะเข้าใจไม่ได้ถ้าคุณยังไม่รู้คำตอบและทำงานย้อนหลัง กฎศัพท์ทั้งหมดถูกกำหนดไว้ด้านหลังโดยสิ่งที่เริ่มต้นโทเค็นใหม่แทนที่จะอธิบายว่าโทเค็นนั้นมีอะไรบ้าง มันเป็นเพียงเรื่องยุ่ง ๆ
Michael Homer

1
@Sergiy ในไวยากรณ์อย่างเป็นทางการการผลิต (หรือกฎการผลิต) อธิบายถึงวิธีที่คุณสามารถสร้างบางสิ่งบางอย่างจากสิ่งอื่น ดูen.wikipedia.org/wiki/Production_%28computer_science%29ดังนั้นcommand : simple_command | compound_command | compound_command redirect_list | function_definition ;การผลิตที่บอกว่าคุณสามารถมีคำสั่งได้ที่ใดมันสามารถเป็นหนึ่งในคำสั่งอย่างง่ายคำสั่งผสมหรือคำสั่งผสมที่มีการเปลี่ยนเส้นทางหรือนิยามฟังก์ชัน
muru

คำตอบ:


6

นั่นเป็นข้อ จำกัด ของวิธีการที่เปลือกแบ่งสายเป็นโทเค็น

เชลล์อ่านบรรทัดจากไฟล์อินพุตและตามส่วนที่ 2 "Shell Introduction"จะแปลงเป็นคำหรือโอเปอเรเตอร์ :

  1. เชลล์แบ่งอินพุตเป็นโทเค็น: คำและตัวดำเนินการ

{เป็นคำที่สงวนไว้

คำบางคำเป็นคำสงวน

คำสงวนคือคำที่มีความหมายพิเศษกับเชลล์ คำต่อไปนี้จะถูกจดจำเป็นคำสงวน:

! { } case do done elif else esac fi for if in then until while

Words, ได้รับการยอมรับว่าเป็นคำต้องถูกคั่นด้วย

คำที่สงวนไว้จะรับรู้ได้ก็ต่อเมื่อมีการคั่น ...

ส่วนใหญ่เป็นช่องว่าง (จุด 7)และตามตัวดำเนินการ

  1. หากอักขระปัจจุบันเป็น <blank> ที่ไม่มีเครื่องหมายอัญประกาศโทเค็นใด ๆ ที่มีอักขระก่อนหน้าจะถูกคั่นและอักขระปัจจุบันจะถูกยกเลิก

(เป็นผู้ประกอบการ

ผู้ประกอบการยืนด้วยตัวเอง :

ในขณะที่ผู้ประกอบการเป็นตัวคั่น

โดยที่"โอเปอเรเตอร์" อยู่ที่ใด :

3.260 Operator

ในภาษาคำสั่งเปลือกทั้งการควบคุมผู้ประกอบการหรือการเปลี่ยนเส้นทางผู้ประกอบการ

ตัวดำเนินการเปลี่ยนเส้นทางคือ :

ผู้ประกอบการเปลี่ยนเส้นทาง

ในภาษาคำสั่งเชลล์โทเค็นที่ทำหน้าที่เปลี่ยนเส้นทาง มันเป็นหนึ่งในสัญลักษณ์ต่อไปนี้:

<     >     >|     <<     >>     <&     >&     <<-     <>

ผู้ประกอบการควบคุมคือ :

3.113 ผู้ควบคุมเครื่อง

ในภาษาคำสั่งเชลล์โทเค็นที่ดำเนินการฟังก์ชันควบคุม มันเป็นหนึ่งในสัญลักษณ์ต่อไปนี้:

&   &&   (   )   ;   ;;   newline   |   ||

ข้อสรุป

ดังนั้น '(' และ ')' เป็นตัวดำเนินการควบคุมในขณะที่ '{' '}' เป็นคำที่สงวนไว้

และคำอธิบายที่ตรงกับคำถามของคุณอยู่ในข้อมูลจำเพาะ :

ตัวอย่างเช่น '(' และ ')' เป็นตัวดำเนินการควบคุมดังนั้นไม่จำเป็นต้องมี <space> ใน (รายการ) อย่างไรก็ตาม '{' และ '}' เป็นคำที่สงวนไว้ใน {list;} ดังนั้นในกรณีนี้จำเป็นต้องมี <space> และ <semicolon> นำหน้า

ซึ่งตรงอธิบายว่าทำไมพื้นที่ (หรือบางตัวคั่นอื่น ๆ ) {จะต้องหลังจากที่

สิ่งนี้ใช้ได้:

{ echo yes;}

เช่นนี้:

{(echo yes);}

นี้:

{(echo yes)}

หรือแม้แต่สิ่งนี้:

{>/dev/tty echo yes;}

คำพูดสุดท้ายคือจุดที่แน่นอน! +1 ฉันจะต้องตรวจสอบคำถามและคำตอบตอนนี้
Sergiy Kolodyazhnyy

13

ความแตกต่างระหว่างวงเล็บปีกกาและวงเล็บว่าวงเล็บ (และ!) เป็นคำสงวนเช่นเดียวfor, if, thenฯลฯ ในขณะที่วงเล็บคือผู้ประกอบการควบคุม คำจะต้องคั่นด้วยช่องว่าง

นั่นหมายความว่าคุณไม่สามารถทำได้

foriin*; do

คุณไม่มี

{somecommand;} >file

หรือ

if !somecommand; then

ช่องว่างที่แสดงในไวยากรณ์ POSIX ไม่ใช่ช่องว่างที่จำเป็นต้องมีในข้อมูลอินพุตเชลล์ แต่เป็นเพียงวิธีการแสดงไวยากรณ์เอง มันเป็นความจริงที่วงเล็บปีกกาเป็นคำที่สงวนไว้ซึ่งหมายความว่าพวกเขาจะต้องล้อมรอบด้วยช่องว่างในขณะที่วงเล็บของ subshell ไม่ได้


1
ดูเหมือนว่าคำตอบนี้สวยมากและฉันเห็นว่ามันพูดว่า "โดยเฉพาะอย่างยิ่งการเป็นตัวแทนรวมระยะห่างระหว่างโทเค็นในบางแห่งที่ <blank> นั้นไม่จำเป็น เพียงหนึ่งคำถาม: มาตรฐานกำหนด(ผู้ประกอบการที่ไหน มันไม่ได้อยู่ในส่วนของไวยากรณ์อย่างน้อย
Sergiy Kolodyazhnyy

@MichaelHomer อา "ผู้ประกอบการควบคุม" ;เช่นเดียวกับ ขอบคุณสำหรับสิ่งนั้น
Kusalananda

ตัวดำเนินการควบคุมจะแสดงอยู่ที่ด้านบนของหน้า man ภายใต้ DEFINITIONS เราอาจมอง()ว่าเป็นตัวดำเนินการควบคุมอย่าง|ที่ทั้งสองเกี่ยวข้องกับ subshells และ{ }ทำงานในเชลล์ปัจจุบันและไม่สามารถเกี่ยวข้องกับเชลล์ย่อยได้
เกล็นแจ็คแมน

@Kusalananda พบส่วนที่ 2.9.2: "หากไพพ์ไลน์เริ่มต้นด้วยคำสงวน! และ command1 เป็นคำสั่ง subshell แอปพลิเคชันจะต้องแน่ใจว่า (ตัวดำเนินการที่จุดเริ่มต้นของ command1 ถูกแยกออกจาก! โดยหนึ่งหรือมากกว่า < อักขระว่าง> พฤติกรรมของคำสงวน! ตามด้วยตัวดำเนินการทันที (ไม่ได้ระบุ "ไม่ได้นิยามชัดเจน แต่มาตรฐานจะเรียกมันว่า(โอเปอเรเตอร์
Sergiy Kolodyazhnyy

@glennjackman ในขณะที่เป็นจริงที่ท่อเกี่ยวข้องกับ subshells แต่นั่นไม่ใช่ประเภทของคำจำกัดความที่เหมาะสม มาตรฐานยังกล่าวถึงว่าในการใช้งานบางอย่างมันก็โอเคสำหรับไพพ์ไลน์ที่จะทำงานในสภาพแวดล้อมการประมวลผลเชลล์ปัจจุบัน (และฉันรู้ว่ามันอยู่ในมาตรฐานเพราะฉันเห็นข้อความเมื่อวานนี้และมองหามันตอนนี้) อย่างไรก็ตามข้อเสนอแนะของคุณทำให้ฉันพบคำพูดที่ฉันแสดงความคิดเห็นข้างต้นซึ่งอย่างน้อยที่สุดมาตรฐานก็เรียกมันว่าโอเปอเรเตอร์แม้ว่าจะไม่ได้กำหนดอย่างชัดเจนว่ามันเป็นหนึ่ง
Sergiy Kolodyazhnyy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.