การแยกคำคืออะไร เหตุใดการเขียนโปรแกรมเชลล์จึงมีความสำคัญ


16

zshฉันได้รับสับสนเกี่ยวกับคำบทบาทแยกเล่นใน ฉันไม่ได้สัมผัสกับแนวคิดนี้เมื่อการเขียนโปรแกรมใน C, Python หรือ MATLAB และสิ่งนี้ได้กระตุ้นความสนใจของฉันว่าทำไมการแยกคำดูเหมือนจะเป็นสิ่งที่เฉพาะเจาะจงสำหรับการเขียนโปรแกรมเชลล์

ฉันได้อ่านเกี่ยวกับการแยกคำในเว็บไซต์นี้และเว็บไซต์อื่น ๆ มาก่อน แต่ไม่พบคำอธิบายที่ชัดเจนเกี่ยวกับแนวคิด Wikipedia มีคำจำกัดความของการแยกคำแต่ดูเหมือนจะไม่มีการอ้างอิงถึงวิธีการนำไปใช้กับ Unix shells

นี่คือตัวอย่างของความสับสนของฉันในzsh:

ในคำถามที่พบบ่อยของZ Shellฉันอ่านต่อไปนี้:

3.1: ทำไมไม่$varที่var="foo bar"ไม่ได้ทำในสิ่งที่ผมคาดหวัง?

ในอนุพันธ์บอร์นเชลล์ส่วนใหญ่ตัวแปรหลายคำเช่น var="foo bar" แบ่งออกเป็นคำต่าง ๆ เมื่อส่งผ่านไปยังคำสั่งหรือใช้ในการfor foo in $varวนซ้ำ โดยค่าเริ่มต้น zsh ไม่ได้มีพฤติกรรมที่: ตัวแปรยังคงเหมือนเดิม (นี่ไม่ใช่ข้อผิดพลาด! ดูด้านล่าง) มีตัวเลือกSH_WORD_SPLITให้ใช้งานร่วมกันได้

อย่างไรก็ตามในคู่มือ Z Shellฉันอ่านต่อไปนี้:

SH_WORD_SPLIT (-y) <K> <S>

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

ทำไมมันไม่บอกว่าSH_WORD_SPLITมีอะไรจะทำอย่างไรกับการแยกคำ? การแยกคำไม่ใช่สิ่งที่ถูกต้องหรือไม่

คำตอบ:


22

Early shells มี data data เพียงชนิดเดียวเท่านั้น แต่เป็นเรื่องปกติที่จะจัดการรายการสตริงโดยทั่วไปเมื่อส่งชื่อไฟล์หลาย ๆ ไฟล์เป็นอาร์กิวเมนต์ไปยังโปรแกรม อีกกรณีการใช้งานทั่วไปสำหรับการแยกคือเมื่อคำสั่งแสดงรายการผลลัพธ์: เอาต์พุตของคำสั่งคือสตริง แต่ข้อมูลที่ต้องการคือรายการของสตริง ในการจัดเก็บรายชื่อไฟล์ในตัวแปรคุณจะต้องเว้นช่องว่างระหว่างชื่อ จากนั้นเชลล์สคริปต์เช่นนี้

files="foo bar qux"
myprogram $files

เรียกว่าmyprogramมีสามข้อโต้แย้งเป็นเปลือกแยกสตริง$filesเป็นคำ ในขณะนั้นช่องว่างในชื่อไฟล์ถูกห้ามหรือพิจารณาอย่างกว้างขวางว่าไม่เสร็จ

เปลือกกรแนะนำอาร์เรย์: คุณสามารถเก็บรายการของสตริงในตัวแปร Korn เชลล์ยังคงเข้ากันได้กับเชลล์ Bourne ที่สร้างขึ้นในตอนนั้นดังนั้นการขยายตัวแปรแบบเปลือยจึงยังคงดำเนินการแยกคำและการใช้อาร์เรย์จำเป็นต้องมีค่าใช้จ่ายด้านไวยากรณ์ คุณจะเขียนตัวอย่างด้านบน

files=(foo bar qux)
myprogram "${files[@]}"

Zsh มีอาร์เรย์ตั้งแต่เริ่มต้นและผู้เขียนเลือกใช้การออกแบบภาษา saner โดยเสียค่าใช้จ่ายในการใช้งานร่วมกันได้แบบย้อนหลัง ใน zsh (ภายใต้กฎการขยายตัวเริ่มต้น) $varไม่ได้เป็นการแบ่งคำให้สมบูรณ์ ถ้าคุณต้องการเก็บรายการคำในตัวแปรคุณควรใช้อาร์เรย์ $=varและถ้าคุณต้องการแยกคำจริงๆคุณสามารถเขียน

files=(foo bar qux)
myprogram $files

ทุกวันนี้ช่องว่างในชื่อไฟล์เป็นสิ่งที่คุณต้องรับมือทั้งคู่เนื่องจากผู้ใช้หลายคนคาดหวังว่ามันจะทำงานและเพราะสคริปต์จำนวนมากถูกดำเนินการในบริบทที่ไวต่อความปลอดภัยซึ่งผู้โจมตีอาจควบคุมชื่อไฟล์ ดังนั้นการแยกคำอัตโนมัติจึงมักเป็นสิ่งที่สร้างความรำคาญ ดังนั้นคำแนะนำทั่วไปของฉันที่จะใช้เครื่องหมายคำพูดคู่เสมอเช่นเขียน"$foo"เว้นแต่คุณจะเข้าใจว่าทำไมคุณถึงต้องการแยกคำในกรณีการใช้งานเฉพาะ (โปรดทราบว่าการขยายตัวแปรเปล่านั้นเกิดขึ้นได้ดีเช่นกัน)


ขอบคุณ Gilles นี่เป็นประโยชน์จริงๆ! มันถูกต้องที่จะบอกว่าพูดประมาณแยกคำแปลงสตริงของรูปแบบ"word1 word2 word3"เป็นรายการ / อาร์เรย์ของรูปแบบ"word1" "word2" "word3"? ฉันได้ปรับปรุง OP ด้วยแหล่งที่มาของความสับสนใน zsh
Amelio Vazquez-Reina

1
@intrpc "การแยกคำ" ไม่ได้แยกคำภาษาธรรมชาติ แต่ใช้$IFSอักขระ ดังนั้น "การแยกฟิลด์" เป็นชื่อที่ดีกว่า แต่ "การแยกคำ" มักใช้สำหรับแนวคิดนี้ในวรรณกรรมเปลือก เอกสาร zsh กำลังพูดเล่นกับคำ
Gilles 'หยุดความชั่วร้าย'

1
ดูเพิ่มเติมrc(plan9 เชลล์, พอร์ตไปยัง Unix) สำหรับการออกแบบที่ดียิ่งขึ้นกว่า zsh เมื่อมันมาถึงตัวแปรและอาร์เรย์
Stéphane Chazelas

3

การแยกคำไม่ได้เจาะจงเฉพาะเปลือก

โปรแกรมส่วนใหญ่ที่ต้องแยกวิเคราะห์ข้อความใช้รูปแบบของการแยกคำเป็นขั้นตอนแรก มันทำก่อนที่จะระบุจาก "คำ" เหล่านี้ตัวเลขผู้ประกอบการสตริงโทเค็นและหน่วยงานที่คล้ายกันสิ่งที่พวกเขาต้องการในการประมวลผล

สิ่งที่เฉพาะเจาะจงกับเชลล์คือพวกมันต้องสร้างรายการอาร์กิวเมนต์ของคำสั่งที่เรียกว่า (C argc / argv, python sys.argv) อย่างถูกต้องรวมถึงการส่งผ่านอาร์กิวเมนต์ด้วยช่องว่างที่ฝังตัวอาร์กิวเมนต์ว่างตัวคั่นที่กำหนดเองและอื่น ๆ เชลล์จำนวนมากใช้ตัวแปร IFS เพื่อให้เกิดความยืดหยุ่น


3

ในกรณีเฉพาะของ Zsh การแยกคำจะถูกกำหนดแตกต่างจากการแยกฟิลด์เล็กน้อย

พิจารณาprog a b cว่ามันจะผ่านสามข้อโต้แย้งไม่ว่าคุณจะตั้งค่าIFSอย่างไร นี่คือการแยกคำ

ถ้าคุณทำเช่นA="a b c"; prog $Aนั้นมันจะผ่านในสามข้อโต้แย้งหากIFSมีช่องว่างหรือหนึ่งอาร์กิวเมนต์เป็นอย่างอื่น นี่คือการแยกฟิลด์

คำจำกัดความที่นี่มีความละเอียดอ่อน สิ่งที่เอกสาร Zsh พยายามที่จะพูดคือแม้ว่าคุณจะปิดการใช้งานตัวเลือกนั้นprog a b cจะยังคงได้รับข้อโต้แย้งแยกต่างหาก (ซึ่งเป็นสิ่งที่ผู้คนคาดหวังเสมอ)


1
Bart Schaefer ผู้พัฒนา zsh ที่ใช้เวลานานยืนยันว่าเป็นความหมายที่แท้จริงของข้อความนั้น
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.