ทำไม nullglob ไม่ผิดนัด?


61

ในหอยส่วนใหญ่nullglobไม่ได้เป็นค่าเริ่มต้น ตัวอย่างเช่นถ้าคุณใช้คำสั่งนี้

ls *

ในไดเรกทอรีว่างมันจะขยาย*glob ไปตามตัวอักษร*แทนที่จะเป็นรายการอาร์กิวเมนต์ว่าง มีวิธีในการเปลี่ยนพฤติกรรมดังกล่าวดังนั้น*ในไดเรกทอรีว่างจะส่งคืนรายการอาร์กิวเมนต์ว่างซึ่งจะดูง่ายขึ้น

ดังนั้นมีเหตุผลทำไมnullglobถูกปิดใช้งานโดยค่าเริ่มต้น? ถ้าเป็นเช่นนั้นเหตุผลอะไร


13
มีคนเคยเลือกที่ไม่ดีที่กลายเป็น "เราทำแบบนี้มาตลอด" ปรากฏการณ์ที่แพร่หลายมาก (ไม่เพียง แต่) ในโลกซอฟต์แวร์
PSkocik

4
เหตุผลเดิมคือไม่มีตัวเลือก nullglob ในตอนนั้น ดังนั้นเพื่อรักษาความเข้ากันได้ย้อนหลังมันจะต้องถูกปิดใช้งานโดยค่าเริ่มต้น
PM 2Ring

3
แม้ว่ามันจะไม่ได้กล่าวถึง null glob โดยเฉพาะ แต่ก็มีบางประวัติเกี่ยวกับ globbing โดยคำตอบนี้: unix.stackexchange.com/a/136409/22724
StrongBad

4
ฉันได้รับความประทับใจบางคนคิดว่าควรเห็นได้ชัดว่า nullglob ควรเปิดใช้งานตามค่าเริ่มต้น ฉันไม่คิดว่ามันชัดเจน การขยายตัวนั้นเกิดขึ้นในเชลล์ก่อนการเรียกใช้คำสั่งหมายความว่าการขยายไปยังสิ่งใด ๆ นั้นเป็นพฤติกรรมที่ใช้งานง่ายน้อยกว่า glob ที่เหลือไม่เปลี่ยนแปลง
kojiro

2
@kojiro ง่ายกับใครน้อยกว่า ทุกคนที่มีความคุ้นเคยกับเชลล์ * NIX รู้ว่านั่น*คือรูปแบบกลมและขยายไปยังไฟล์ที่มีอยู่ทั้งหมด เป็นอย่างไร "ใช้งานง่าย" เพื่อให้มีกรณีพิเศษที่ "ขยาย" ไดเรกทอรีว่างเปล่าเป็นตัวอักษร*?
Kyle Strand

คำตอบ:


78

nullglobตัวเลือก (ซึ่ง BTW เป็นzshสิ่งประดิษฐ์เพียงเพิ่มปีต่อไปbash( 2.0)) จะไม่เหมาะในหลายกรณี และlsเป็นตัวอย่างที่ดี:

ls *.txt

หรือเทียบเท่าที่ถูกต้องมากขึ้น:

ls -- *.txt

ด้วยnullglobon จะทำงานlsโดยไม่มีอาร์กิวเมนต์ซึ่งถือว่าเป็นls -- .(แสดงรายการไดเรกทอรีปัจจุบัน) หากไม่มีไฟล์ที่ตรงกันซึ่งอาจแย่กว่าการเรียกlsด้วยตัวอักษร*.txtเป็นอาร์กิวเมนต์

คุณมีปัญหาคล้ายกันกับยูทิลิตี้ข้อความส่วนใหญ่:

grep foo *.txt

จะมองหาfoostdin หากไม่มีtxtไฟล์

ค่าเริ่มต้นที่สมเหตุสมผลมากขึ้นและหนึ่งใน csh, tcsh, zsh หรือ fish 2.3+ (และของกระสุน Unix ตอนต้น) คือการยกเลิกคำสั่งโดยสิ้นเชิงหาก glob ไม่ตรงกัน

bash(ตั้งแต่รุ่น 3) มีfailglobตัวเลือกสำหรับการที่ (น่าสนใจที่จะสนทนานี้ตั้งแต่ขัดต่อash, AT & T kshหรือzsh, bashไม่สนับสนุนขอบเขตท้องถิ่นสำหรับตัวเลือก ( แต่ที่มีการเปลี่ยนแปลงได้ใน 4.4) ตัวเลือกที่สามารถใช้งานได้ทั่วโลกไม่ทำลายบางสิ่ง เช่นฟังก์ชั่น bash-completion)

โปรดทราบว่า csh tcsh และจะแตกต่างกันเล็กน้อยจากzsh, fishหรือbash -O failglobในกรณีเช่น:

ls -- *.txt *.html

ในกรณีที่คุณต้องการ globs ทั้งหมดที่ไม่ตรงกันเพื่อให้คำสั่งถูกยกเลิก ตัวอย่างเช่นหากมีไฟล์ txt หนึ่งไฟล์และไม่มีไฟล์ html นั่นจะกลายเป็น:

ls -- file.txt

คุณจะได้รับกับพฤติกรรมที่zshมีsetopt cshnullglobแต่วิธีที่เหมาะสมมากขึ้นที่จะทำมันในzshจะใช้ glob เช่น:

ls -- *.(txt|html)

ในzshและksh93คุณยังสามารถใช้nullglobบนพื้นฐานต่อซึ่งเป็นวิธี saner มากมายกว่าการปรับเปลี่ยนการตั้งค่าระดับโลก:

files=(*.txt(N))  # zsh
files=(~(N)*.txt) # ksh93

จะสร้างอาร์เรย์ว่างถ้าไม่มีtxtไฟล์แทนที่จะล้มเหลวคำสั่งด้วยข้อผิดพลาด (หรือทำให้เป็นอาร์เรย์ที่มี*.txtอาร์กิวเมนต์หนึ่งตัวที่มีเชลล์ตัวอื่น)

รุ่นfishก่อนหน้า 2.3 จะทำงานเหมือนbash -O nullglobแต่ให้คำเตือนเมื่อมีการโต้ตอบเมื่อ glob ไม่ตรงกัน ตั้งแต่ 2.3 มันทำงานเหมือนzshยกเว้นสำหรับใช้ใน globs for, หรือsetcount

ตอนนี้ในบันทึกประวัติศาสตร์พฤติกรรมนั้นได้ถูกทำลายโดยเชลล์เป้าหมาย ในเวอร์ชันก่อนหน้าของ Unix นั้นการทำ globbing นั้นทำได้ผ่านตัว/etc/globช่วยและผู้ช่วยนั้นทำตัวเหมือนcsh: มันจะล้มเหลวในการออกคำสั่งหากไม่มี globs ที่ตรงกับไฟล์ใด ๆ และลบ globs โดยไม่ต้องจับคู่กัน

ดังนั้นสถานการณ์ที่เราอยู่ในวันนี้เกิดจากการตัดสินใจที่ไม่ถูกต้องในเชลล์เป้าหมาย

โปรดทราบว่าเชลล์เป้าหมาย (และเชลล์ C) มาพร้อมกับคุณสมบัติ Unix ใหม่: สภาพแวดล้อม นั่นหมายความว่าการขยายตัวของตัวแปร (มันบรรพบุรุษเท่านั้นมี$1, $2... พารามิเตอร์ตำแหน่ง) เชลล์เป้าหมายแนะนำการทดแทนคำสั่งด้วย

อีกประการหนึ่งการตัดสินใจการออกแบบที่ดีของบอร์นเปลือกก็จะดำเนินการ globbing (และแยก) เมื่อการขยายตัวของตัวแปรและแทนคำสั่ง (อาจจะเข้ากันได้ย้อนหลังกับเปลือก ธ อมป์สันที่echo $1ยังคงเรียก/etc/globถ้า$1สัญลักษณ์ที่มีอยู่ (มันเป็นมากขึ้นเช่นก่อนการประมวลผลการขยายตัวแมโคร ที่นั่นเช่นเดียวกับในค่าที่ขยายได้ถูกแยกวิเคราะห์อีกครั้งเป็นรหัสเชลล์))

ความล้มเหลวที่ไม่ตรงกันจะหมายถึงตัวอย่างเช่น:

pattern='a.*b'
grep $pattern file

จะล้มเหลวคำสั่ง (เว้นแต่มีบางa.whateverbไฟล์ในไดเรกทอรีปัจจุบัน) csh(ซึ่งยังดำเนินการ globbing เมื่อการขยายตัวของตัวแปร) ไม่ล้มเหลวคำสั่งการในกรณีที่ (และฉันเถียงจะดีกว่าออกจากข้อผิดพลาดอยู่เฉยๆมีแม้ถ้ามันไม่ดีเท่าที่ไม่ได้ทำ globbing ที่ทุกคนชอบในzsh)


มีปัญหาการใช้งานอื่น: nullglobดูเหมือนว่าการแบ่งแท็บเสร็จสมบูรณ์ (การกดปุ่มแท็บจะไม่ทำอะไรเลยเมื่อเปิดใช้งาน)
Kyle Strand

1
มันเป็นไปได้ที่จะใช้ nullglob บนต่อ globพื้นฐานกับทุบตี - แม้ว่าไวยากรณ์ไม่เป็นที่สง่างามเป็น zshfiles=$(shopt -s nullglob;echo *.txt)
จอน Nalley

2
@JonNalley ที่เก็บการต่อข้อมูล (พร้อมช่องว่าง) ของชื่อไฟล์ (ด้วยการแปลงที่เป็นไปได้ด้วยxpg_echo) ลงในตัวแปรสเกลาร์ คุณจะต้องสิ่งที่ชอบreadarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)กับbash4.4 หรือสูงกว่าหรือ(shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmdมี GNU xargsเพื่อที่จะสามารถใช้งานได้ในทุกที่มีชื่อไฟล์โดยพลการ หรือยังกับ bash4.4 ให้ใช้ฟังก์ชันตัวช่วยที่ใช้local -(คัดลอกมาจากเถ้า 25 ปีต่อมา) สำหรับขอบเขตภายในสำหรับตัวเลือก
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.