Dlaczego znak wieloznaczny * tak różni się między poleceniami zip i rm?


58

Złożyłem skrypt, aby wykonać dla mnie pewne operacje na plikach. Korzystam z operatora wieloznacznej karty, *aby zastosować funkcje do wszystkich plików danego typu, ale jest jedna rzecz, której nie rozumiem. Mogę unzipwszystkie pliki w takim folderze

unzip "*".zip

Jednak, aby później usunąć wszystkie pliki zip, muszę to zrobić

rm *.zip

Oznacza to, że nie chce znaków cudzysłowu. Z drugiej strony rozpakowanie nie działa, jeśli po prostu dam mu * (ostrzega mnie, że „pliki nie zostały dopasowane”).

Dlaczego to jest inne? Wydaje mi się, że to dokładnie ta sama operacja. A może źle używam dzikiej karty?

Przedstawienia dzikiej karty w Uniksie tak naprawdę nie wchodzą w to, i nie mogłem znaleźć niczego w dokumentach rmani zip.

Korzystam z terminala na komputerze Mac (Yosemite).


4
Nie miałem pojęcia, że ​​mogę unzipto zrobić bez normalnej for f in *.zip;do...donepętli powłoki. Taki dziwny interfejs użytkownika, który nie przypomina unixa.
Peter Cordes

@Peter Myślę, że źle zrozumiałeś sytuację. unzipstosuje glob do zawartości archiwum; nie można ich zdobyć z paczki za pomocą symbolu wieloznacznego. (Będziesz potrzebować `` dla f in unzip -l archive.zip; do ... gotowe ')
Alexis

@alexis: Wiedziałem o unzipakceptowaniu globów w jednym pliku zip. Ale to jest inne; W rzeczywistości próbowałem unzip '*.zip'w katalogu z wieloma plikami zip i wyodrębnia wszystkie pliki ze wszystkich zamków błyskawicznych. Tak jak powiedziałem, super dziwne. tarnie ma takiego trybu działania.
Peter Cordes

1
@ Peter Widzę ... tak, to dziwne, zwłaszcza, że ​​rozpakuj nie akceptuje wielu argumentów wiersza poleceń! Oczywiście implementacja tylko dla systemu Windows. Źle zinterpretowałem opis zadania w OP.
Alexis

1
@alexis: PKZip wcześniej niż Windows . Jest to program wiersza polecenia DOS, wydany po raz pierwszy w 1989 roku. Port Unix używa zasadniczo tego samego kodu parsującego cmdline, AFAIK.
Peter Cordes

Odpowiedzi:


68

Bardzo dobrze wyjaśniłeś sytuację. Ostatni element układanki jest w stanie unzipporadzić sobie z symbolami wieloznacznymi:

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

ARGUMENTY

plik [.zip]

...

Wyrażenia symboli wieloznacznych są podobne do tych obsługiwanych w powszechnie używanych powłokach uniksowych (sh, ksh, csh) i mogą zawierać:

* dopasowuje ciąg 0 lub więcej znaków

Cytując * symbol wieloznaczny, uniemożliwiłeś jego powłoce rozwinięcie go, dzięki czemu zobaczysz unzipznak wieloznaczny i zajmie się rozszerzaniem go zgodnie z własną logiką.

rm, Natomiast nie obsługuje symboli wieloznacznych na własną rękę , więc próbuje zacytować wieloznaczny poinstruuje rmszukać dosłownym gwiazdką w pliku zamiast.

Przyczyną, unzip *.zipktóra nie działa, jest taka unzipskładnia, która po prostu nie pozwala na wiele plików zip; jeśli istnieje wiele parametrów, oczekuje się, że drugi i kolejne będą plikami w archiwum:

rozpakuj [-Z] [-cflptTuvz [abjnoqsCDKLMUVWX $ /: ^]] plik [.zip] [plik (i) ...] [-x plik (i) ...] [-d exdir]


6
dzięki, to ma sens! jeśli dobrze rozumiem, w jednym przypadku mówię unzipwłasnym językiem, w drugim przypadku ogólne żargon unixowy?
Patrick

6
Poprawny. Ważne jest, aby pamiętać, co robi twoja powłoka, a co program.
Jeff Schaller

7
pkzip powstał w systemie DOS, który nie rozwijał symboli wieloznacznych przekazywanych do programów.
Thorbjørn Ravn Andersen

11
@ Patryk unikalnym sposobem przetwarzania wielu plików za pomocą programu, który może pracować tylko z jednym plikiem na raz, jest użycie pętli. np for f in *.zip ; do unzip -v "$f" ; done. i duża część powodów, dla których powłoka sama rozszerza nazwę pliku itp., jest taka, że ​​każdy program nie musi tego robić (co skutkowałoby mnóstwem niezależnie napisanych implementacji interpretacji symboli wieloznacznych, które różniłyby się na małe, ale irytujące) .
cas

25

Różnica między tymi dwoma poleceniami polega na cytowanym *znaku. Jeśli wywołasz polecenie w powłoce i użyjesz *znaku jako argumentu, sama powłoka oceni argument. Zobacz ten przykład:

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

Teraz z *:

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

Powłoka ocenia symbol wieloznaczny i buduje polecenie w następujący sposób:

$ ls file1.zip  file2.zip  file3.zip

Z cytowanym znakiem wieloznacznym jest interpretowany jako plik o nazwie (dosłownie) *.zip:

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

Nie unzipmożna wywołać narzędzia z wieloma spakowanymi plikami jako argumentami. Ale twórca wybrał na to inny sposób. Z strony podręcznika:

plik [.zip]

[...] Wyrażenia symboli wieloznacznych są podobne do wyrażeń obsługiwanych w powszechnie używanych powłokach uniksowych (sh, ksh, csh) [...] ( Pamiętaj, aby podać dowolny znak, który inaczej mógłby zostać zinterpretowany lub zmodyfikowany przez system operacyjny , szczególnie pod Unix i VMS.)


Czy wiesz, dlaczego autorzy unzipzdecydowali się pójść tą drogą, a nie dopuszczać wielu spakowanych plików jako argumentów?
David Etler

@DavidEtler Też nie wiem.
chaos

1
Nie potrafię powiedzieć, dlaczego @DavidEtler, ale po kompilacji składnia unzip akceptuje nazwy plików po pliku zip, które są uważane za zawartość tego pliku zip. Byłoby dwuznaczne, czy chciałbyś, aby drugi plik zip był parametrem „rozpakuj mnie” czy „rozpakuj ten wewnętrzny plik zip z poprzedniego archiwum”.
Jeff Schaller

@DavidEtler nie wiem, co myślą deweloperzy, ale wtedy wszystko było znacznie wolniejsze i mniejsze. Zwykle nie masz do czynienia z więcej niż jednym plikiem zip na raz. Miałeś dyskietki o pojemności 90 lub 250 kB i naprawdę cieszyłeś się z dysku o pojemności 10 MB. Rzeczy zostały skompresowane, ponieważ musiały być, nie tylko w transporcie międzysystemowym.
Joe

7

Różnica polega na tym, że w pierwszym przypadku sama powłoka rozszerza glob:

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

podczas gdy w drugim przypadku sama aplikacja robi coś o tym dosłownym charakterze:

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

Jeśli nie jest cytowany, powłoka najpierw rozszerza glob, a polecenie zostanie uruchomione z tym, do czego rozszerza się glob globu.


2

Polecenie otrzyma argumenty po przetworzeniu przez powłokę.

Przy pierwszym przetwarzaniu niecytowana *zostanie rozwinięta przez powłokę (do listy plików w bieżącym katalogu (pwd), które pasują do wzorca):

echo *.zip

Wyświetla listę wszystkich .zipplików. Ale nieecho "*".zip" będzie .

Przy pierwszym przetwarzaniu cytowany "*"nie zostanie rozwinięty, zostanie przekazany do polecenia rozpakuj jako parametr (po usunięciu cytowania). Polecenie unzip otrzyma parametr *.zip:

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

Jest to polecenie rozpakuj, które rozszerza *listę plików.


Interesujące jest również to, że te dwa polecenia nie wykonają dokładnie tej samej czynności końcowej i kto rozszerzy *zmiany:

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

Pierwsze polecenie otrzymuje *.ziprozszerzenie, które przetwarza wszystkie pliki. Drugie polecenie unzipotrzyma listę wszystkich .zipplików w pwd, których nie przetworzy, ponieważ programista rozpakujący postanowił odrzucić rozszerzenie więcej niż jednego zippliku.


0

Cytaty są potrzebne, ponieważ zip obsługuje wiele argumentów:

rm: usuń wszystkie pliki z listy argumentów

zip: rozpakuj plik w pierwszym argumencie. wyodrębnij pliki tylko z pozostałych argumentów.

$ 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

jak widać, próbuje znaleźć file2.zip i file3.zip w pliku file1.zip

aby umożliwić wyodrębnienie wielu plików zip jednocześnie, zip obsługuje interpretację globu z innym rezultatem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.