การขยายด้วย * .txt ในเชลล์ไม่ทำงานหากไม่มีไฟล์. txt อยู่


10

ฉันกำลังเล่นกับการขยายตัวและฉันสังเกตเห็นพฤติกรรมที่แปลกประหลาด ฉันพยายามทำ:

echo ./*.txt

และฉันไม่มีไฟล์. txt ใด ๆ ในไดเรกทอรีปัจจุบันของฉัน ผลลัพธ์ที่ฉันได้รับคือ:

./*.txt

ฉันแค่อยากรู้อยากเห็น: ทำไมฉันถึงได้รับนี้ ฉันคาดหวังว่าจะไม่ได้รับผลลัพธ์ใด ๆ

PS: เมื่อฉันมี.txtไฟล์การขยายถูกตีความอย่างถูกต้อง กล่าวอีกนัยหนึ่งว่าฉันมีไฟล์smthn.txtเสียงสะท้อนจริงcurrent_directory/smthn.txt

คำตอบ:


15

การดัดแปลงจากหน้า man bash shell

bash สแกนแต่ละคำเพื่อหาอักขระ *,?, และ [. หากหนึ่งในอักขระเหล่านี้ปรากฏขึ้นคำนั้นจะถือเป็นรูปแบบและแทนที่ด้วยรายการชื่อไฟล์ที่เรียงตามตัวอักษรที่ตรงกับรูปแบบ หากไม่พบชื่อไฟล์ที่ตรงกันและไม่ได้เปิดใช้งานตัวเลือกเชลล์ nullglob คำนั้นจะไม่เปลี่ยนแปลง หากตั้งค่าตัวเลือก nullglob และไม่พบข้อมูลที่ตรงกันคำนั้นจะถูกลบ

ในกรณีนี้ฉันสันนิษฐานว่า nullglob ไม่ได้เปิดใช้งานดังนั้นคำจะไม่เปลี่ยนแปลง - ดังนั้นผลลัพธ์ที่คุณเห็น


2
คุณสามารถเปลี่ยนพฤติกรรมดังต่อไปนี้: shopt -s nullglobจะให้สตริงที่ว่างสำหรับรูปแบบที่ไม่ตรงกันและshopt -u nullglob(การตั้งค่ามาตรฐาน) จะให้รูปแบบตัวเอง
PerlDuck

13

ฉันคาดหวังว่าจะไม่ได้รับผลลัพธ์ใด ๆ

หากnullglobมีการเริ่มต้นคำสั่งจำนวนมากจะมีพฤติกรรมค่อนข้างไม่คาดคิดเพราะมันเป็น (อาจจะโชคร้าย) ทั่วไปที่คำสั่งรักษากรณีของศูนย์การขัดแย้งชื่อไฟล์ในทางที่แตกต่างกันในเชิงคุณภาพมากกว่ากรณีของหนึ่งหรือมากกว่าหนึ่งข้อโต้แย้งชื่อไฟล์

สมมติว่าคุณได้เปิดใช้งานnullglob( shopt -s nullglob) *.txtและคุณอยู่ในไดเรกทอรีที่ไฟล์ไม่ตรงกับ จากนั้น*.txtจะขยายเป็นไม่มีอะไร - ไม่ใช่ฟิลด์ว่างเปล่า แต่ไม่มีฟิลด์เลย - ตามที่คุณคาดไว้ แต่นั่นจะมีผลลัพธ์เหล่านี้:

  • ls *.txtจะรายการทุกไฟล์ในไดเรกทอรีปัจจุบัน (ยกเว้นไฟล์ที่ซ่อนอยู่) เพราะนั่นคือสิ่งที่lsไม่เมื่อคุณไม่ผ่านมันอาร์กิวเมนต์ชื่อไฟล์ใด ๆ
  • cat *.txtจะอ่านจากอินพุตมาตรฐานเพราะเมื่อไม่มีข้อโต้แย้งชื่อไฟล์ก็เป็นถ้าคุณวิ่งcat cat -หากทำงานแบบโต้ตอบมันจะนั่งรออินพุต คำสั่งหลายตัวทำงานในลักษณะนี้
  • cp *.txt dest/cp: missing destination file operand after 'dest/'จะล้มเหลวด้วยข้อผิดพลาด นี่ไม่ใช่ภัยพิบัติ แต่มันค่อนข้างสับสนและแตกต่างจากความสำเร็จเงียบ ๆ ที่อาจเป็นที่ต้องการ
  • file *.txtและโปรแกรมอื่น ๆ ที่ไม่มีลักษณะพิเศษสำหรับกรณีของอาร์กิวเมนต์ชื่อไฟล์ที่เป็นศูนย์จะยังคงล้มเหลวพร้อมกับข้อผิดพลาดหรือข้อความการใช้งานเมื่อไม่มีการส่งผ่าน
  • แม้แต่กรณีที่รู้สึกว่าพวกเขาควรจะทำงานบ่อยๆ printf 'Got file: "%s"\n' *.txtจะพิมพ์Got file: ""แทนไม่มีอะไร
  • ความล้มเหลวไม่ได้ตั้งใจที่จะพูดการเกิดขึ้นของ*, ?และ[ที่ไม่ได้ตั้งใจที่จะขยายได้โดยเปลือกจะมากขึ้นมักจะก่อให้เกิดผลที่ไม่ถูกต้องอย่างเห็นได้ชัด แต่ในรูปแบบที่อาจจะยากที่จะคิดออก ตัวอย่างเช่นถ้าชื่อไฟล์ในไดเรกทอรีปัจจุบันไม่มีการเริ่มต้นด้วยgeditแล้วapt list gedit*(ซึ่งapt list 'gedit*'ก็ตั้งใจ) จะกลายเป็นเพียงapt listและรายชื่อทุกแพคเกจใช้ได้

ดังนั้นจึงเป็นเรื่องดีที่คุณจะไม่ได้รับพฤติกรรมนี้โดยไม่ได้ขอมัน อาจจะมากที่สุดสถานการณ์จริงทั่วไปที่มีความเรียบง่ายจริงโดยมีnullglob for f in *.txtดูคำถามนี้ด้วย ( คำตอบของ Sergiy Kolodyazhnyyเชื่อมโยงกับ)

คำถามที่ยากที่จะตอบคือทำไมfailglob- ทุกที่มันเป็นข้อผิดพลาดในการขยายตัวเพื่อให้มี glob ที่ไม่ตรงกับไฟล์ใด ๆ - ไม่ใช่ค่าเริ่มต้นในการทุบตี ฉันเชื่อว่าคำตอบของ Sergiy Kolodyazhnyyสามารถจับเหตุผลได้แม้ว่าจะไม่ได้กล่าวถึงโดยตรงก็ตาม การรักษา globs ที่ไม่ขยายตัวโดยไม่ก่อให้เกิดข้อผิดพลาดในการขยายตัว (อาจเป็นเรื่องที่น่าเสียดาย) คือพฤติกรรมที่เป็นมาตรฐานและเป็นพฤติกรรมปกติ แม้ว่า bash จะไม่พยายามทำตาม POSIX อย่างสมบูรณ์เว้นแต่ว่ามันถูกเรียกด้วยชื่อshหรือผ่าน--posixตัวเลือก แต่ตัวเลือกการออกแบบจำนวนมากถึงแม้ว่าจะไม่ได้อยู่ในโหมด POSIX ก็ตาม POSIX โดยตรง พวกเขาต้องเลือกพฤติกรรมบางอย่างและมีข้อเสียที่เกี่ยวข้องกับการขัดต่อความคาดหวังของผู้ใช้


ฉันคิดว่านี่เป็นแง่มุมที่มีอิทธิพลน้อยที่สุดในประวัติศาสตร์ของเรื่องดังนั้นฉันจึงบันทึกไว้ในที่สุด ... แต่มันก็คุ้มค่าที่จะพูดถึงว่ามีบางอย่างที่แปลก ๆ เกี่ยวกับnullglobพฤติกรรม

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

และยังมีความไม่ลงรอยกันที่ลึกซึ้งยิ่งขึ้นอีกหนึ่งอย่างที่nullglobไม่ได้กล่าวถึง กรณีของอักขระศูนย์แบบวงกลม ("อักขระตัวแทน") จะถือว่าแตกต่างอย่างมากจากที่หนึ่งหรือสองหรือหมายเลขอื่น ๆ ตัวอย่างเช่นshopt -s nullglobหากab?d?fไม่ตรงกับไฟล์ใด ๆ ไฟล์นั้นจะถูกลบออก หากab?dไม่ตรงกับไฟล์ใด ๆ ไฟล์นั้นจะถูกลบออก แต่หากabไม่ตรงกับไฟล์ใด ๆ (เช่นหากไม่มีไฟล์ที่มีชื่อตรงab) ไฟล์นั้นจะยังไม่ถูกลบ แน่นอนว่ามันจะเป็นความหายนะถ้ามันถูกลบออกไปเพราะมันอาจไม่ได้ตั้งใจที่จะอ้างถึงไฟล์ที่มีอยู่ในไดเรกทอรีปัจจุบันเลย มันอาจไม่ได้อ้างถึงไฟล์ แต่สิ่งนี้ยังช่วยขจัดความหวังใด ๆ เพื่อความมั่นคงโดยรวม

สามพฤติกรรมทุบตีให้ - เริ่มต้นของการรักษา globs ที่ไม่ตรงกับไฟล์ใด ๆ ราวกับว่าพวกเขาไม่ได้ globs และผ่านพวกเขาไม่ได้ขยายพฤติกรรมที่คุณคาดว่าจะรักษาพวกเขา (ถ้าคุณจะให้อภัยวลีแปลก ๆ นี้) ในฐานะที่บ่งบอกถึงศูนย์ทั้งหมดของไฟล์ที่จับคู่ ( nullglob) และพฤติกรรมที่ปลอดภัยในการพิจารณาข้อผิดพลาด ( failglob) - ทั้งหมดเป็นตัวแทนของวิธีการที่แตกต่างกันไปสู่ความคลุมเครือในเชลล์ไม่สามารถรู้ได้ว่าคำใด ๆ ชื่อไฟล์. เชลล์ทำการขยายโดยไม่มีความรู้ว่าคำสั่งเฉพาะที่คุณเรียกใช้นั้นจะจัดการกับอาร์กิวเมนต์อย่างไร

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


เห็นได้ชัดว่าทุบตี-4.3.39 (2) failglobไม่ได้มี ดังนั้นจึงไม่สามารถเริ่มต้นได้เนื่องจากไม่ได้รับการสนับสนุนเสมอ
Ruslan

6

เหตุผลหลักคือเพราะเป็นพฤติกรรมมาตรฐานที่ระบุโดยPOSIX - มาตรฐานซึ่งครอบคลุมเปลือกภาษาคำสั่งและเหนือสิ่งอื่นใดจับคู่รูปแบบ (เช่นเปลือกหอยbash, dashเปลือก - เริ่มต้นของ Ubuntu /bin/shและkshทำตามมาตรฐานนี้) จากส่วนที่2.13.3 รูปแบบที่ใช้สำหรับการขยายชื่อไฟล์ :

หากรูปแบบไม่ตรงกับชื่อไฟล์หรือชื่อพา ธ ที่มีอยู่สตริงรูปแบบจะต้องไม่มีการเปลี่ยนแปลง

หลักสูตรนี้มีผลข้างเคียง - ชื่อไฟล์ที่ตรงกันซึ่งอาจเป็นตัวอักษร*.txtอย่างแท้จริง nullglobตัวเลือกในbashและzshสามารถช่วย: ถ้าตัวเลือกที่มีการเปิดใช้งานผ่านshopt -s nullglob(และมันก็ไม่ได้เปิดใช้งานโดยเริ่มต้นที่นำไปใช้กับคำถามนี้) แล้ว GLOBSTAR จะขยายไปยังสตริงว่างเมื่อไม่มีชื่อไฟล์ที่ตรงกัน ksh93มีกลไกการจับคู่รูปแบบขั้นสูงของตัวเองซึ่งให้ผลเหมือนกัน~(N)*.txt

ดูเพิ่มเติมเหตุใด nullglob จึงไม่ใช่ค่าเริ่มต้น

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