Warum unterscheidet sich das Platzhalterzeichen * zwischen den Befehlen zip und rm?

58

Ich habe ein Skript zusammengestellt, um einige Dateioperationen für mich durchzuführen. Ich verwende den Platzhalter-Operator *, um Funktionen auf alle Dateien eines Typs anzuwenden, aber eines verstehe ich nicht. Ich kann unzipalle Dateien in einem solchen Ordner speichern

unzip "*".zip

Um danach jedoch alle zip-Dateien zu entfernen, muss ich Folgendes tun

rm *.zip

Das heißt, die Anführungszeichen werden nicht benötigt. Das Entpacken funktioniert dagegen nicht, wenn ich nur das * gebe (warnt mich, dass "Dateien nicht übereinstimmen").

Warum ist das anders? Für mich scheint dies genau die gleiche Operation zu sein. Oder verwende ich den Platzhalter falsch?

Einführungen in die Wildcard in Unix gehen nicht wirklich darauf ein, und ich konnte in den Dokumenten rmoder nichts finden zip.

Ich benutze das Terminal auf einem Mac (Yosemite).

Patrick
quelle
4
Ich hatte keine Ahnung, unzipdass dies ohne die normale for f in *.zip;do...doneShell-Schleife möglich wäre. Solch eine seltsame, nicht unixartige Befehlszeilen-Benutzeroberfläche.
Peter Cordes
@ Peter Ich glaube du verstehst die Situation falsch. unzipWendet den Glob auf den Inhalt eines Archivs an. Sie können sie nicht mit einem Platzhalter erhalten. (Du brauchst `` `für f in unzip -l archive.zip; do ... done`)
alexis
@alexis: Ich wusste unzip, dass Globs akzeptiert werden, die in einer einzelnen Zip-Datei enthalten sind. Das ist aber anders; Ich habe es tatsächlich unzip '*.zip'in einem Verzeichnis mit mehreren Zip-Dateien versucht , und es extrahiert alle Dateien aus allen Zip-Dateien. Wie ich schon sagte, super komisch. tarhat keine solche Betriebsart.
Peter Cordes
1
@ Peter Ich verstehe ... ja, das ist komisch, zumal unzip nicht mehrere Kommandozeilenargumente akzeptiert! Ganz klar eine reine Windows-Implementierung. Ich habe die Beschreibung der Aufgabe im OP falsch interpretiert.
Alexis
1
@alexis: PKZip datiert Windows vor . Es handelt sich um ein DOS-Befehlszeilenprogramm, das erstmals 1989 veröffentlicht wurde. Der Unix-Port verwendet im Grunde denselben cmdline-Parsing-Code, AFAIK.
Peter Cordes

Antworten:

69

Sie haben die Situation sehr gut erklärt. Das letzte Teil des Puzzles ist, dass unzipWildcards selbst verarbeitet werden können:

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

ARGUMENTE

Datei [.zip]

...

Platzhalterausdrücke ähneln denen, die in häufig verwendeten Unix-Shells (sh, ksh, csh) unterstützt werden, und können Folgendes enthalten:

* entspricht einer Folge von 0 oder mehr Zeichen

Indem Sie den Platzhalter * in Anführungszeichen setzen, haben Sie verhindert, dass Ihre Shell ihn erweitert, sodass unzipder Platzhalter angezeigt wird und er nach seiner eigenen Logik erweitert wird.

rmIm Gegensatz dazu werden Platzhalter allein nicht unterstützt. Wenn Sie also versuchen, einen Platzhalter in Anführungszeichen rmzu setzen, müssen Sie stattdessen nach einem wörtlichen Sternchen im Dateinamen suchen.

Der Grund, warum unzip *.zipdies nicht funktioniert, ist, dass unzipdie Syntax einfach nicht mehrere ZIP-Dateien zulässt. Wenn es mehrere Parameter gibt, werden für den zweiten und die folgenden Parameter Dateien im Archiv erwartet:

entpacken [-Z] [-cflptTuvz [abjnoqsCDKLMUVWX $ /: ^]] Datei [.zip] [Datei (en) ...] [-x xDatei (en) ...] [-d exdir]

Jeff Schaller
quelle
6
Danke, das macht Sinn! wenn ich es richtig verstehe, spreche ich in einem unzipfall die eigene sprache, in dem anderen fall den allgemeinen unix lingo?
Patrick
6
Richtig. Es ist wichtig, sich vor Augen zu halten, was Ihre Shell im Vergleich zu einem Programm tut.
Jeff Schaller
7
pkzip stammte von DOS, das die an Programme übergebenen Platzhalter nicht erweiterte.
Thorbjørn Ravn Andersen
11
@patrick Die Unix-Methode zum Verarbeiten mehrerer Dateien mit einem Programm, das jeweils nur mit einer Datei arbeiten kann, besteht darin, eine Schleife zu verwenden. zb for f in *.zip ; do unzip -v "$f" ; done. und ein großer Teil des Grundes, warum die Shell die Dateinamenerweiterung usw. selbst ausführt, ist nicht das, was jedes einzelne Programm tun muss. .
cas
24

Der Unterschied zwischen diesen beiden Befehlen ist das in Anführungszeichen gesetzte *Zeichen. Wenn Sie einen Befehl in einer Shell aufrufen und das *Zeichen für ein Argument verwenden, wird das Argument von der Shell selbst ausgewertet. Siehe dieses Beispiel:

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

Jetzt mit einem *:

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

Die Shell wertet den Platzhalter aus und erstellt einen Befehl wie folgt:

$ ls file1.zip  file2.zip  file3.zip

Mit einem Platzhalter in Anführungszeichen wird es als Datei mit dem Namen (wörtlich) interpretiert *.zip:

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

Das unzipDienstprogramm kann nicht mit mehreren komprimierten Dateien als Argumente aufgerufen werden. Der Entwickler entschied sich jedoch für einen anderen Weg. Aus der Manpage:

Datei [.zip]

[...] Wildcard-Ausdrücke ähneln denen, die in häufig verwendeten Unix-Shells (sh, ksh, csh) [...] unterstützt werden. ( Stellen Sie sicher, dass Sie alle Zeichen angeben, die vom Betriebssystem anders interpretiert oder geändert werden könnten , insbesondere unter Unix und VMS.)

Chaos
quelle
Wissen Sie, warum die Autoren unzipdiesen Weg gewählt haben, anstatt mehrere gezippte Dateien als Argumente zuzulassen?
David Etler
@ DavidEtler Ich weiß es auch nicht.
Chaos
1
Ich kann auch nicht sagen, warum, @DavidEtler, aber unzips Syntax akzeptiert Dateinamen nach der Zip-Datei, von denen angenommen wird, dass sie Inhalt dieser Zip-Datei sind. Es wäre mehrdeutig, ob Sie für eine zweite Zip-Datei einen "Entpacke mich" -Parameter oder einen "Entpacke diese interne Zip-Datei aus dem vorhergehenden Archiv" -Parameter vorsahen.
Jeff Schaller
@ DavidEtler weiß nicht, was die Entwickler dachten, aber damals war alles viel langsamer und kleiner. Normalerweise haben Sie nicht mehr als eine Zip-Datei gleichzeitig bearbeitet. Sie hatten Disketten mit 90 oder 250 KB und waren wirklich froh, ein 10-MB-Laufwerk zu haben. Die Dinge wurden komprimiert, weil sie sein mussten, nicht nur für den systemübergreifenden Transport.
Joe
6

Der Unterschied besteht im ersten Fall darin, dass die Shell selbst den Glob erweitert:

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

Im zweiten Fall tut die Anwendung selbst etwas mit diesem wörtlichen Zeichen:

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

Ohne Anführungszeichen erweitert die Shell zuerst den Glob, und der Befehl wird mit dem Shell-Glob ausgeführt, zu dem er erweitert wurde.

Thrig
quelle
2

Ein Befehl erhält die Argumente, nachdem sie von der Shell verarbeitet wurden.

Bei der ersten Verarbeitung wird ein nicht *in Anführungszeichen gesetztes Zeichen durch die Shell erweitert (auf die Liste der Dateien im aktuellen Verzeichnis (pwd), die dem Muster entsprechen):

echo *.zip

Listet alle .zipDateien auf. Aber echo "*".zip"wird nicht .

Bei der ersten Verarbeitung wird ein Anführungszeichen "*"nicht erweitert, sondern als Parameter an den Befehl unzip übergeben (nachdem das Anführungszeichen entfernt wurde). Der Befehl unzip erhält einen Parameter von *.zip:

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

Es ist der Befehl unzip, der *die Liste der Dateien erweitert.


Interessant ist auch, dass mit diesen beiden Befehlen nicht genau dieselbe endgültige Aktion ausgeführt wird und wer die *Änderungen erweitert :

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

Der erste Befehl erhält einen, *.zipden er erweitert, um alle Dateien zu verarbeiten. Der zweite Befehl unziperhält eine Liste aller .zipDateien im pwd, die nicht verarbeitet werden, da der Entpacker die Erweiterung mehrerer Dateien abgelehnt hat zip.


quelle
0

Die Anführungszeichen werden benötigt, da zip mehrere Argumente verarbeitet:

rm: Alle Dateien in der Argumentliste entfernen

zip: entpacke die Datei im ersten Argument. extrahieren Sie nur die Dateien in den verbleibenden Argumenten.

$ 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

Wie Sie sehen, wird versucht, file2.zip und file3.zip in file1.zip zu finden

Damit Sie mehrere ZIP-Dateien gleichzeitig extrahieren können, unterstützt zip die Interpretation des Globus für sich mit einem anderen Ergebnis.

eMBee
quelle