Mengapa karakter wild card * sangat berbeda antara perintah zip dan rm?

58

Saya mengumpulkan skrip untuk melakukan beberapa operasi file untuk saya. Saya menggunakan operator kartu liar *untuk menerapkan fungsi ke semua file jenis, tetapi ada satu hal yang saya tidak dapatkan. Saya bisa unzipsemua file dalam folder seperti ini

unzip "*".zip

Namun, untuk menghapus semua file zip sesudahnya, perlu saya lakukan

rm *.zip

Artinya, tidak ingin tanda kutip. Unzip, di sisi lain, tidak berfungsi jika saya hanya memberikan * (beri saya peringatan bahwa "file tidak cocok").

Mengapa ini berbeda? Bagi saya, ini sepertinya operasi yang sama persis. Atau saya salah menggunakan wild card?

Pengantar kartu liar di Unix tidak benar-benar masuk ke ini, dan saya tidak dapat menemukan apa pun di rmatau zipdokumen.

Saya menggunakan terminal pada Mac (Yosemite).

patrick
sumber
4
Saya tidak tahu unzipbisa melakukan ini tanpa for f in *.zip;do...donelingkaran shell normal . UI baris perintah yang non-unix-like aneh.
Peter Cordes
@ Peter Saya pikir Anda salah paham situasinya. unzipberlaku glob untuk isi arsip; Anda tidak bisa mendapatkannya dari bash dengan wildcard. (Anda perlu `` `untuk f in unzip -l archive.zip; do ... done`)
alexis
@alexis: Saya tahu tentang unzipmenerima gumpalan yang cocok dengan dalam satu file zip. Tetapi ini berbeda; Saya sebenarnya mencoba unzip '*.zip'dalam direktori dengan banyak file zip, dan itu mengekstrak semua file dari semua zip. Seperti yang saya katakan, sangat aneh. tartidak memiliki mode operasi seperti itu.
Peter Cordes
1
@Peter saya mengerti ... ya itu aneh, terutama karena unzip tidak akan menerima beberapa argumen commandline! Jelas implementasi Windows saja. Saya salah mengartikan deskripsi tugas OP.
alexis
1
@alexis: PKZip pra-tanggal Windows . Ini adalah program baris perintah DOS, pertama kali dirilis pada tahun 1989. Port Unix pada dasarnya menggunakan kode parsing cmdline yang sama, AFAIK.
Peter Cordes

Jawaban:

68

Anda telah menjelaskan situasinya dengan sangat baik. Bagian terakhir dari teka-teki ini adalah yang unzipdapat menangani wildcard itu sendiri:

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

ARGUMEN

file [.zip]

...

Ekspresi wildcard mirip dengan yang didukung di shell Unix yang umum digunakan (sh, ksh, csh) dan mungkin mengandung:

* cocok dengan urutan 0 karakter atau lebih

Dengan mengutip * wildcard, Anda mencegah shell Anda mengembangkannya, sehingga unzipmelihat wildcard dan berurusan dengan mengembangkannya sesuai dengan logikanya sendiri.

rm, sebaliknya, tidak mendukung wildcard sendiri , jadi mencoba mengutip wildcard akan menginstruksikan rmuntuk mencari tanda bintang literal dalam nama file sebagai gantinya.

Alasan yang unzip *.ziptidak berfungsi adalah karena unzipsintaks tidak memungkinkan untuk banyak file zip; jika ada beberapa parameter, ia mengharapkan yang ke-2 dan selanjutnya menjadi file dalam arsip:

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

Jeff Schaller
sumber
6
terima kasih, itu masuk akal! jika saya mengerti benar, dalam satu kasus saya berbicara unzipbahasa sendiri, dalam kasus lain istilah unix umum?
patrick
6
Benar. Penting untuk diingat apa yang dilakukan shell Anda vs apa yang dilakukan suatu program.
Jeff Schaller
7
pkzip berasal dari DOS yang tidak memperluas wildcard yang diteruskan ke program.
Thorbjørn Ravn Andersen
11
@patrick cara unix memproses banyak file dengan program yang hanya bisa bekerja dengan satu file pada satu waktu adalah dengan menggunakan loop. mis for f in *.zip ; do unzip -v "$f" ; done. dan sebagian besar alasan MENGAPA shell melakukan ekspansi nama file dll itu sendiri adalah sot bahwa setiap program individu tidak harus (yang akan menghasilkan memiliki banyak implementasi ekspansi wildcard yang ditulis secara independen yang berbeda dalam cara yang kecil namun menjengkelkan) .
cas
25

Perbedaan antara kedua perintah tersebut adalah *karakter yang dikutip . Jika Anda memanggil perintah di shell dan menggunakan *karakter untuk argumen, shell itu sendiri akan mengevaluasi argumen. Lihat contoh ini:

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

Sekarang dengan *:

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

Shell mengevaluasi wildcard dan membuat perintah sebagai berikut:

$ ls file1.zip  file2.zip  file3.zip

Dengan wildcard yang dikutip, ia ditafsirkan sebagai file bernama (secara harfiah) *.zip:

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

The unziputilitas tidak bisa disebut dengan beberapa file zip sebagai argumen. Tapi, pengembang memilih cara lain untuk ini. Dari halaman manual:

file [.zip]

[...] Ekspresi wildcard mirip dengan yang didukung di shell Unix yang umum digunakan (sh, ksh, csh) [...] ( Pastikan untuk mengutip karakter apa pun yang mungkin ditafsirkan atau dimodifikasi oleh sistem operasi , terutama di bawah Unix dan VMS.)

kekacauan
sumber
Apakah Anda tahu mengapa penulis unzipmemilih untuk memilih rute itu, daripada mengizinkan beberapa file zip sebagai argumen?
David Etler
@ DavidEtler saya tidak tahu juga.
kekacauan
1
Saya juga tidak bisa mengatakan mengapa, @DavidEtler, tetapi as-built, sintaks unzip menerima nama file setelah zipfile yang diasumsikan sebagai isi dari zipfile itu. Ini akan menjadi ambigu apakah Anda bermaksud untuk file zip kedua menjadi parameter "unzip saya" atau "membongkar file zip internal ini dari arsip sebelumnya".
Jeff Schaller
@ Davidvidtler tidak tahu apa yang dipikirkan pengembang, tapi semuanya jauh lebih lambat dan lebih kecil saat itu. Anda biasanya tidak berurusan dengan lebih dari satu file zip sekaligus. Anda memiliki disket yang menampung 90 atau 250kB dan Anda benar-benar senang memiliki disk drive 10MB. Hal-hal dikompresi karena mereka harus, bukan hanya untuk transportasi antar sistem.
Joe
7

Perbedaannya adalah pada kasus pertama shell itu sendiri memperluas glob:

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

sementara dalam kasus kedua aplikasi itu sendiri Melakukan Sesuatu ™ dengan karakter literal itu:

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

Jika tidak dikutip, shell pertama memperluas glob, dan perintah akan dijalankan dengan apa pun yang glob glob diperluas.

thrig
sumber
2

Perintah akan menerima argumen setelah diproses oleh shell.

Pada pemrosesan pertama, tanda kutip *akan diperluas oleh shell (ke daftar file di direktori ini (pwd) yang cocok dengan pola):

echo *.zip

Akan mencantumkan semua .zipfile. Tetapi tidakecho "*".zip" akan .

Pada pemrosesan pertama, kutipan "*"tidak akan diperluas, itu akan diberikan kepada perintah unzip sebagai parameter (setelah penawaran dihapus). Perintah unzip akan menerima parameter *.zip:

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

Ini adalah perintah unzip yang memperluas *ke daftar file.


Menarik juga bahwa kedua perintah ini tidak akan melakukan tindakan final yang sama persis, dan siapa yang memperluas *perubahan:

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

Perintah pertama menerima perintah *.zipyang diperluas untuk memproses semua file. Perintah kedua unzipakan menerima daftar semua .zipfile di pwd, yang tidak akan diproses, karena pengembang unzip telah memilih untuk menolak perluasan lebih dari satu zipfile.


sumber
0

Kutipan diperlukan karena cara zip menangani banyak argumen:

rm: hapus semua file dalam daftar argumen

zip: unzip file dalam argumen pertama. hanya ekstrak file dalam argumen yang tersisa.

$ 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

seperti yang Anda lihat, ia mencoba mencari file2.zip dan file3.zip di dalam file1.zip

untuk memungkinkan Anda mengekstraksi banyak file zip sekaligus, zip mendukung interpretasi glob dengan sendirinya, dengan hasil yang berbeda.

eMBee
sumber