เหตุใดอักขระ wild card * จึงแตกต่างกันระหว่างคำสั่ง zip และ rm


58

ฉันรวบรวมสคริปต์เพื่อดำเนินการบางอย่างกับฉัน ฉันใช้โอเปอเรเตอร์การ์ด*เพื่อใช้ฟังก์ชั่นกับไฟล์ทุกประเภท แต่มีสิ่งหนึ่งที่ฉันไม่ได้รับ ฉันสามารถunzipไฟล์ทั้งหมดในโฟลเดอร์เช่นนี้

unzip "*".zip

อย่างไรก็ตามในการลบไฟล์ zip ทั้งหมดหลังจากนั้นฉันต้องทำ

rm *.zip

นั่นคือมันไม่ต้องการเครื่องหมายคำพูด unzip ในทางกลับกันไม่สามารถใช้งานได้หากฉันให้ * (ให้คำเตือนว่า "ไฟล์ไม่ตรงกัน")

ทำไมถึงแตกต่างกัน? สำหรับฉันดูเหมือนว่าการดำเนินการเดียวกันแน่นอน หรือฉันใช้การ์ดไวด์ไม่ถูกต้อง?

การแนะนำให้รู้จักกับไวด์การ์ดใน Unix นั้นไม่ได้ทำสิ่งนี้จริงๆและฉันไม่สามารถหาตำแหน่งใด ๆ ในเอกสารrmหรือzip

ฉันใช้เทอร์มินัลกับ Mac (Yosemite)


4
ฉันไม่รู้ว่าunzipจะทำสิ่งนี้ได้อย่างไรหากไม่มีfor f in *.zip;do...doneเชลล์วงวนปกติ UI บรรทัดคำสั่งแปลก ๆ ที่ไม่เหมือนยูนิเซฟ
Peter Cordes

@ ปีเตอร์ฉันคิดว่าคุณเข้าใจผิดสถานการณ์ unzipใช้ glob กับเนื้อหาของการเก็บถาวร; คุณไม่สามารถรับพวกเขาจากการทุบตีด้วยตัวแทน (คุณต้องการ `` `for f in unzip -l archive.zip; do ... done`)
alexis

@alexis: ฉันรู้เกี่ยวกับการunzipยอมรับ globs ให้เข้ากับไฟล์ zip ไฟล์เดียว แต่นี่แตกต่าง ฉันลองunzip '*.zip'ในไดเรกทอรีที่มีไฟล์ zip หลายไฟล์และแยกไฟล์ทั้งหมดจากรหัสไปรษณีย์ทั้งหมด อย่างที่ฉันพูดไปมันแปลกมาก tarไม่มีโหมดการทำงานใด ๆ เช่นนั้น
Peter Cordes

1
@ ปีเตอร์ฉันเห็นว่า ... ใช่แล้วมันแปลกโดยเฉพาะอย่างยิ่งเมื่อเปิดเครื่องรูดจะไม่ยอมรับข้อโต้แย้งหลายบรรทัด! เห็นได้ชัดว่ามีการติดตั้ง Windows เท่านั้น ฉันตีความคำอธิบายของงาน OP ผิดไป
อเล็กซิส

1
@alexis: PKZip Windowsล่วงหน้า มันเป็นโปรแกรมบรรทัดคำสั่งของ DOS ซึ่งเปิดตัวครั้งแรกในปี 1989 พอร์ต Unix ใช้รหัส cmdline-parsing เดียวกัน AFAIK
Peter Cordes

คำตอบ:


69

คุณอธิบายสถานการณ์ได้ดีมาก ชิ้นสุดท้ายของปริศนาคือunzipสามารถจัดการสัญลักษณ์แทนได้:

http://www.info-zip.org/mans/unzip.html

อาร์กิวเมนต์

แฟ้ม [.zip]

...

นิพจน์ไวด์การ์ดนั้นคล้ายกับที่รองรับในเชลล์ Unix ที่ใช้กันทั่วไป (sh, ksh, csh) และอาจมี:

* ตรงกับลำดับ 0 หรือมากกว่าตัวละคร

ด้วยการอ้างถึง * wildcard คุณจะป้องกันเชลล์ของคุณไม่ให้ขยายเพื่อให้unzipเห็น wildcard และดีลนั้นขยายตามตรรกะของมันเอง

rmในทางกลับกันไม่สนับสนุนสัญลักษณ์แทนด้วยตนเองดังนั้นการพยายามอ้างอิงอักขระตัวแทนจะแนะนำrmให้ค้นหาเครื่องหมายดอกจันที่แท้จริงในชื่อไฟล์แทน

สาเหตุที่unzip *.zipใช้งานไม่ได้ก็คือunzipไวยากรณ์ไม่อนุญาตให้มีไฟล์ zip หลายไฟล์ หากมีหลายพารามิเตอร์คาดว่าตัวที่สองและตัวที่ตามมาจะเป็นไฟล์ในไฟล์เก็บถาวร:

unzip [-Z] [-cflptTuvz [abjnoqsCDKLMUVWX $ /: ^]] ไฟล์ [.zip] [ไฟล์ (s) ... ] [-x xfile (s) ... ] [-d exdir]


6
ขอบคุณที่เหมาะสม! ถ้าฉันเข้าใจถูกต้องในกรณีหนึ่งฉันกำลังพูดภาษาunzipของตัวเองในกรณีอื่น ๆ ศัพท์แสงทั่วไป?
Patrick

6
แก้ไข. โปรดจำไว้ว่าเชลล์ของคุณทำอะไรกับโปรแกรมที่ทำ
Jeff Schaller

7
pkzip มาจาก DOS ซึ่งไม่ได้ขยาย wildcard ที่ส่งผ่านไปยังโปรแกรม
Thorbjørn Ravn Andersen

11
@patrick วิธี unix ในการประมวลผลหลายไฟล์ด้วยโปรแกรมที่สามารถทำงานกับไฟล์ครั้งละหนึ่งไฟล์เท่านั้นคือการใช้ลูป for f in *.zip ; do unzip -v "$f" ; doneเช่น และส่วนใหญ่ของเหตุผลที่เชลล์ทำการขยายชื่อไฟล์ ฯลฯ นั้นเองก็ไม่จำเป็นที่จะต้องมีแต่ละโปรแกรม (ซึ่งจะส่งผลให้มีการใช้งานการเขียนเสริมอิสระของการขยายไวด์การ์ดที่แตกต่างกันในรูปแบบที่เล็ก แต่น่ารำคาญ) .
cas

24

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

$ ls
file1.zip  file2.zip  file3.zip  file4.txt

ตอนนี้ด้วย*:

$ ls *.zip
file1.zip  file2.zip  file3.zip

เชลล์ประเมิน wildcard และสร้างคำสั่งดังต่อไปนี้:

$ ls file1.zip  file2.zip  file3.zip

ด้วยไวด์การ์ดที่ยกมามันถูกตีความว่าเป็นไฟล์ชื่อ (ตัวอักษร) *.zip:

$ ls "*".zip
ls: cannot access *.zip: No such file or directory

unzipยูทิลิตี้ไม่สามารถเรียกว่ากับไฟล์ซิปหลายเป็นข้อโต้แย้ง แต่ผู้พัฒนาเลือกวิธีอื่นสำหรับสิ่งนี้ จาก manpage:

แฟ้ม [.zip]

[... ] การแสดงออกของ Wildcard นั้นคล้ายกับที่รองรับใน Unix shells ที่ใช้กันทั่วไป (sh, ksh, csh) [... ] ( ให้แน่ใจว่าได้อ้างอิงอักขระที่อาจตีความหรือแก้ไขโดยระบบปฏิบัติการโดยเฉพาะอย่างยิ่งภายใต้ Unix และ VMS)


คุณรู้หรือไม่ว่าทำไมผู้แต่งunzipเลือกที่จะไปเส้นทางนั้นแทนที่จะอนุญาตให้มีการบีบอัดไฟล์หลายไฟล์เป็นข้อโต้แย้ง?
David Etler

@DavidEtler ฉันก็ไม่รู้เหมือนกัน
ความโกลาหล

1
ฉันไม่สามารถพูดได้ว่าทำไม @DavidEtler แต่ไวยากรณ์ที่ unzip สร้างขึ้นเป็นตัวยอมรับชื่อไฟล์หลังจากไฟล์ zip ที่ถือว่าเป็นเนื้อหาของไฟล์ zip นั้น มันอาจจะคลุมเครือไม่ว่าคุณจะต้องการให้ไฟล์ zip ตัวที่สองเป็นพารามิเตอร์ "unzip me" หรือ "unpack ไฟล์ zip ภายในนี้จากไฟล์เก็บถาวรก่อนหน้านี้"
Jeff Schaller

@DavidEtler ไม่ทราบว่าสิ่งที่นักพัฒนามีความคิด แต่ทุกอย่างเป็นมากช้าลงและมีขนาดเล็กกลับมาแล้ว โดยปกติคุณไม่ได้จัดการไฟล์ซิปมากกว่าหนึ่งไฟล์ในแต่ละครั้ง คุณมีฟล้อปปี้ที่ถือ 90 หรือ 250kB และคุณมีความสุขมากที่มีดิสก์ไดรฟ์ 10MB สิ่งที่ถูกบีบอัดเพราะพวกเขาจะต้องไม่เพียง แต่สำหรับการขนส่งระหว่างระบบ
Joe

6

ความแตกต่างคือในกรณีแรกที่เชลล์ขยายตัว glob:

% cd /                                                       
% echo *
Applications Library Network System Users Volumes bin cores ...
% 

ในขณะที่ในกรณีที่สองแอปพลิเคชันตัวเองทำอะไรบางอย่าง™ด้วยตัวอักษรที่แท้จริง:

% cd /
% perl -E 'chdir "/tmp" or die; say for glob($ARGV[0])' "*"
com.apple.launchd.aj4FEhYqm5
...

หากไม่มีการอ้างอิงเชลล์จะขยาย glob ออกก่อนและคำสั่งจะรันด้วยสิ่งที่เชลล์ glob ขยายออกไป


2

คำสั่งจะรับอาร์กิวเมนต์หลังจากที่ประมวลผลโดยเชลล์

ในการประมวลผลครั้งแรก*เชลล์ที่unquote จะถูกขยายโดยเชลล์ (ไปยังรายการไฟล์ในไดเร็กทอรีปัจจุบัน (pwd) ที่ตรงกับรูปแบบ):

echo *.zip

จะแสดงรายการ.zipไฟล์ทั้งหมด แต่echo "*".zip"จะไม่ได้

ในการประมวลผลครั้งแรกการเสนอราคา"*"จะไม่ถูกขยายมันจะถูกกำหนดให้กับคำสั่ง unzip เป็นพารามิเตอร์ (หลังจากการลบข้อความทั้งหมด) คำสั่งเปิดเครื่องรูดจะได้รับพารามิเตอร์*.zip:

$ echo unzip "*".zip
unzip *.zip

มันเป็นคำสั่งเปิดเครื่องรูดซึ่งขยาย*ไปยังรายการของไฟล์


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

unzip "*".zip                ### the command unzip expands `*.zip`.
unzip *.zip                  ### the shell expands `*.zip`.

คำสั่งแรกได้รับ*.zipซึ่งจะขยายการประมวลผลไฟล์ทั้งหมด คำสั่งที่สองunzipจะได้รับรายชื่อของ.zipไฟล์ทั้งหมดใน pwd ซึ่งจะไม่ดำเนินการตามที่นักพัฒนา unzip เลือกที่จะปฏิเสธการขยายมากกว่าหนึ่งzipไฟล์


0

จำเป็นต้องใช้เครื่องหมายคำพูดเนื่องจากวิธีการที่ zip จัดการกับอาร์กิวเมนต์หลายตัว:

rm: ลบไฟล์ทั้งหมดในรายการอาร์กิวเมนต์

zip: แตกไฟล์ในอาร์กิวเมนต์แรก แตกไฟล์ในอาร์กิวเมนต์ที่เหลือเท่านั้น

$ ls *.zip
file1.zip  file2.zip  file3.zip
$ unzip *.zip
Archive:  file1.zip
caution: filename not matched:  file2.zip
caution: filename not matched:  file3.zip

อย่างที่คุณเห็นมันพยายามค้นหา file2.zip และ file3.zip ภายใน file1.zip

เพื่อให้คุณสามารถแยกไฟล์ zip หลายไฟล์พร้อมกันได้ zip สนับสนุนการตีความกลมกลืนด้วยตัวเองพร้อมกับผลลัพธ์ที่แตกต่าง

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