ทำไมไม่มี ";" หลังจาก "ทำ" ในลูป sh


28

เหตุใดจึงไม่มี;ตัวอักษรdoในเชลล์วนเมื่อเขียนในบรรทัดเดียว

นี่คือสิ่งที่ฉันหมายถึง เมื่อเขียนบนหลายบรรทัดforลูปจะมีลักษณะดังนี้:

$ for i in $(jot 2)
> do
>     echo $i
> done

และในบรรทัดเดียว:

$ for i in $(jot 2); do echo $i; done

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

เช่นเดียวกันกับwhileลูปเกินไป

$ while something
> do
>     anotherthing
> done
$ while something; do anotherthing; done


2
มันสอดคล้องกัน: ไม่มีอัฒภาคหลังจากwhile/ forอย่างใดอย่างหนึ่ง
Dmitry Grigoryev

สิ่งที่อยู่ในใจทันทีคือมันไม่จำเป็น ที่อื่นอัฒภาคแยกคำแถลง
พอลเดรเปอร์

เพราะมันจะซ้ำซ้อน ไม่มีความกำกวมหากปราศจากมัน
user207421

คำตอบ:


29

นั่นคือไวยากรณ์ของคำสั่ง ดูคำสั่งผสม

for name [ [in [words …] ] ; ] do commands; done

หมายเหตุเฉพาะ: do commands

คนส่วนใหญ่ใส่doและcommandsในบรรทัดที่แยกต่างหากเพื่อให้สามารถอ่านได้ง่ายขึ้น แต่มันไม่จำเป็นที่คุณสามารถเขียน:

for i in thing
do something
done

ฉันรู้ว่าคำถามนี้เกี่ยวกับเชลล์โดยเฉพาะและฉันเชื่อมโยงกับคู่มือทุบตี มันไม่ได้เขียนแบบนั้นในคู่มือเชลล์ แต่เขียนในบทความที่เขียนโดย Stephen Bourneสำหรับนิตยสาร byte

สตีเฟ่นพูดว่า:

รายการคำสั่งเป็นลำดับหนึ่งหรือง่ายขึ้นคำสั่งแยกหรือยกเลิกโดยการขึ้นบรรทัดใหม่หรือ;(อัฒภาค) นอกจากนี้คำสงวนชอบทำและทำนำหน้าตามปกติโดยการขึ้นบรรทัดใหม่หรือ;... ในแต่ละครั้งที่เปิดรายชื่อคำสั่งดังต่อไปนี้ทำจะถูกดำเนินการ


5
มันจะเป็นที่น่าสนใจว่าทำไมมีไม่ต้องfor i in thing; do; something; doneเป็นอัฒภาคเช่น อนุญาตให้ใช้การแบ่งบรรทัดได้ แต่ไม่อนุญาตให้ใช้เครื่องหมายอัฒภาค
rexkogitans

@gidds: ใช่แน่นอน นั่นก็คือโครงสร้างไวยากรณ์เชลล์ เรื่องsomethingนี้เป็นเรื่องและdoนำไปใช้กับมัน เหมือนในโครงสร้างประโยคภาษาอังกฤษ
Peter Cordes

@gidds ตัวอักษร (หรือบางส่วนของไวยากรณ์อื่น ๆ ) เป็นสิ่งจำเป็นเนื่องจากคำสั่งหลายคำสั่งสามารถไปในเงื่อนไข (ในwhileไวยากรณ์) ดังนั้นคุณต้องมีสิ่งที่จะแยกความแตกต่างคำสั่งเงื่อนไขจากคำสั่งร่างกายห่วง การใช้doการแยกพวกมันอาจเป็นสิ่งที่เหมาะสมที่สุด
JoL

@rexkogitans แน่นอนฉันไม่เคยรู้ว่านั่นเป็นข้อผิดพลาดทางไวยากรณ์ คำถามชื่อนี้เหมาะมากสำหรับเรื่องนั้นเช่นกัน มันอาจจะเกี่ยวข้องกับการไม่อนุญาตให้มีร่างกายที่ว่างเปล่า คุณได้รับข้อผิดพลาดทางไวยากรณ์ที่แตกต่างกันเล็กน้อยเมื่อสร้างเนื้อหาว่างด้วยบรรทัดใหม่และตัวอย่างของคุณไม่มีเนื้อหาว่างเปล่า
JoL

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

12

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

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

ที่นี่doคำหลักจำเป็นต้องแยกส่วนเงื่อนไขและเนื้อหาหลักของลูปออกจากกัน doเป็นคำสำคัญเหมือนwhile, ifและthen(และdone) และมันน่าจะเป็นในทิศทางเดียวกับคนอื่น ๆ ที่มันไม่จำเป็นต้องอัฒภาคหรือขึ้นบรรทัดใหม่หลังจากที่มัน แต่ดูเหมือนทันทีก่อนที่คำสั่ง

ทางเลือก (ทั่วไปifและwhile) จะดูน่าเกลียดเราจะต้องเขียนเช่น

if; [ -n "$a" ]; then
    some command here
fi

ไวยากรณ์ของforลูปคล้ายกัน


11

ไวยากรณ์ของเชลล์เป็นพื้นฐานของคำนำหน้า มันมีข้อแนะนำโดยคำหลักพิเศษ คำสั่งบางอย่างต้องไปด้วยกัน

การwhileวนซ้ำทำอย่างน้อยหนึ่งคำสั่งการทดสอบ:

test ; test ; test ; ...

และโดยหนึ่งหรือมากกว่าหนึ่งคำสั่งร่างกาย:

body ; body ; body ; ...

มีบางอย่างที่ต้องบอกเชลล์ว่าการวนรอบสักครู่เริ่มต้นขึ้น นั่นคือจุดประสงค์ของwhileคำว่า:

while test ; test ; test ; ...

แต่แล้วสิ่งต่าง ๆ ที่คลุมเครือ คำสั่งใดเป็นจุดเริ่มต้นของร่างกาย? มีบางอย่างที่ต้องระบุและนั่นคือสิ่งที่ส่วนdoนำหน้าใช้:

do body ; body ; body ; ...

และในที่สุดสิ่งที่จะต้องบ่งชี้ว่ามีการเห็นร่างสุดท้าย; คำหลักพิเศษ doneทำเช่นนั้น

คำหลักเชลล์เหล่านี้ไม่ต้องการการแยกอัฒภาคแม้ในบรรทัดเดียวกัน ตัวอย่างเช่นถ้าคุณปิดลูปซ้อนกันหลาย ๆ done done done ...คุณสามารถเพียงแค่มี

ค่อนข้างอัฒภาคอยู่ระหว่าง... test ; body ... ถ้าพวกเขาอยู่ในบรรทัดเดียวกัน อัฒภาคที่จะเข้าใจ Terminator: testมันเป็นกับ ดังนั้นหากคำหลักที่ถูกแทรกระหว่างพวกเขาก็มีไประหว่างอัฒภาคและdo bodyถ้าอยู่อีกด้านหนึ่งของอัฒภาคมันจะถูกฝังอย่างผิดพลาดภายในtestไวยากรณ์ของคำสั่งแทนที่จะวางไว้ระหว่างคำสั่ง

ไวยากรณ์เปลือกที่ถูกออกแบบมาโดยสตีเฟ่นบอร์นและเป็นแรงบันดาลใจAlgol บอร์นรักอัลกอลมากจนเขาใช้มาโคร C จำนวนมากในซอร์สโค้ดของเชลล์เพื่อทำให้ C ดูเหมือนอัลกอล คุณสามารถเรียกดูแหล่งเปลือก 1979 ลงวันที่ 7 จากเวอร์ชั่นระบบปฏิบัติการยูนิกซ์ มาโครอยู่ในmac.hและพวกเขาจะใช้ทั่วสถานที่ ยกตัวอย่างเช่นifงบจะกลายเป็นIF... ELSE... ...ELIFFI


ซอร์สโค้ดนั้นน่าประทับใจ
keithpjolley

ใช่มันเป็นทัวร์เดอบังคับของ cpp
ขโมย

7

ฉันขอยืนยันว่าdoไม่พิเศษที่นี่โดยเฉพาะ คุณได้รับข้อผิดพลาดเนื่องจาก;ไม่สามารถเริ่มรายการคำสั่งได้ ถ้าเป็นเช่นนั้นคำสั่งแรกจะว่างเปล่าและไวยากรณ์ไม่อนุญาตให้ใช้คำสั่งว่าง (การกำหนดตัวแปรหรือการเปลี่ยนเส้นทางโดยไม่มีคำสั่งใช่ แต่ไม่มีอะไรแน่นอนไม่มี) สิ่งนี้เป็นจริงในหลาย ๆ ที่ไม่ว่าจะมีรายการคำสั่งใด:

$ while true; do ; echo foo; done
dash: 1: Syntax error: ";" unexpected
$ while ; true; do; echo; done
dash: 1: Syntax error: ";" unexpected
$ { ; echo foo; }
dash: 1: Syntax error: ";" unexpected
$ if ; true; then echo foo; fi
dash: 1: Syntax error: ";" unexpected

แม้:

$ ; ;
dash: 1: Syntax error: ";" unexpected

ในทุกสถานที่เหล่านี้คาดว่าจะมีรายการคำสั่งและการนำหน้า;จึงไม่คาดคิด


ใช่ - วิธีที่ฉันเพิ่ม '\ n' หลังจาก 'ทำ' โดยพลการทำให้ฉันคิดว่า 'ทำ' เป็นโทเค็นสแตนด์อะโลนและไม่ใช่สิ่งที่ทำงานบนบล็อกที่ตามมา
keithpjolley

มันเป็นวิธีการขึ้นบรรทัดใหม่ที่ไม่ใช่หนี (ซึ่งมิฉะนั้นดูเหมือนว่าจะทำงานเช่นเดียวกับอัฒภาคในไวยากรณ์เปลือก) จะได้รับอนุญาตหลังdo(และifอื่น ๆ ) แต่?
Henning Makholm

@Henning บรรทัดใหม่จำนวนใด ๆ ที่ได้รับอนุญาตก่อน (และหลัง) รายการคำสั่ง การขึ้นบรรทัดใหม่และอัฒภาคทำหน้าที่แตกต่างกันในด้านอื่น ๆ เช่นกัน ตัวอย่างเช่นการขยายนามแฝงจะกระทำเมื่ออ่านบรรทัดอินพุตทั้งหมด (เช่นจนถึงบรรทัดใหม่ถัดไป) และไม่ใช่ต่อคำสั่ง
muru

3

ไวยากรณ์คือ:

for i in [whitespace separated list of values]
do [newline separated list of commands]
done

บรรทัดใหม่ใด ๆ ไม่ว่าจะอยู่ในรายการคำสั่งหรือก่อนหน้า "do" หรือ "Done" สามารถถูกแทนที่ด้วย ";"

ขึ้นบรรทัดใหม่ (แต่ไม่ใช่ ";") ได้รับอนุญาตหลังจาก "ทำ" และก่อนหน้า "ใน" เพียงเพื่อทำให้สิ่งต่าง ๆ ดูดีขึ้น แต่มันก็ไม่สมเหตุสมผลที่จะต้องขึ้นบรรทัดใหม่ที่นั่น


3

if คล้ายกับคำหลัก: ค่อนข้างอ่านได้เมื่อคำสั่งสั้น:

if   test_commands
then true_commands
else false_commands
fi

เมื่อฉันรู้ว่าฉันถูกวางลงโดยพลการ\nหลังจากdoนั้นก็สมเหตุสมผล (ตราบใดที่คุณไม่คิดว่าจะไม่สามารถวางอำนาจ\nระหว่างคำสั่งและ args อื่น ๆ )
keithpjolley

ดีdo, then, elseไม่ได้args - ถ้าพวกเขาคุณจะไม่ได้รับอนุญาตให้ใช้เครื่องหมายอัฒภาคก่อนหน้าพวกเขา นี่คือคีย์เวิร์ดของเชลล์ (ดูtype if then elif else fi)
glenn jackman

ฉันเดาว่าฉันยังไม่ชัดเจนเท่าที่ฉันต้องการที่จะตอบ
keithpjolley

ประเด็นของฉันคือ "ทำ" ไม่เหมือน "คำสั่งอื่น ๆ " ดังนั้นจึงเชื่อฟังกฎที่แตกต่างกัน
เกล็นแจ็

3

คำตอบสั้น ๆ เพราะนี้จะถูกระบุโดยPOSIX เปลือกไวยากรณ์ สิ่งใดระหว่างdoและdoneถือเป็นกลุ่มของงบในขณะที่;เป็นตัวคั่นตามลำดับ:

for_clause       : For name                                      do_group
                 | For name                       sequential_sep do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group

do_group         : Do compound_list Done 

sequential_sep   : ';' linebreak
                 | newline_list

นอกจากนี้หาก;จำเป็นมาตรฐานจะระบุอย่างชัดเจนดังนั้นและจะรวมถึงsequential_sepหลังจากDoนั้น


1
@drewbenn คุณหมายถึงคนแรกมากใช่ไหม นั่นคือการวนซ้ำผ่านพารามิเตอร์ตำแหน่ง ดังนั้นหากคุณมีสคริปต์ชื่อ like ./myscript.sh abc โดยที่ abc เป็นอาร์กิวเมนต์ให้วนรอบสำหรับ i; ทำเสียงสะท้อน "$ i"; ทำจะห่วงผ่าน abc แต่ผมยังคิดว่ามันจะต้องอัฒภาคให้มันทำงาน
Sergiy Kolodyazhnyy

ตกลงดังนั้นความสงสัยของฉันก็ถูกลบเช่นกัน
Sergiy Kolodyazhnyy

1

เพราะ do คือคีย์เวิร์ดและสิ่งที่ตามมาคือพารามิเตอร์ ล่าม Bash รู้ดีกว่า มันคล้ายกับว่าไม่มี ';' ติดตาม i ในคำสั่ง for คุณสามารถเพิ่มบรรทัดใหม่หลังจาก i ในและพิมพ์ที่เหลือบนบรรทัดใหม่และมันจะทำงานได้ดี

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