Pourquoi le caractère générique * est-il si différent entre les commandes zip et rm?

58

J'ai mis en place un script pour faire quelques opérations de fichier pour moi. J'utilise l'opérateur générique *pour appliquer des fonctions à tous les fichiers d'un type, mais il y a une chose que je ne comprends pas. Je peux unziptous les fichiers dans un dossier comme celui-ci

unzip "*".zip

Cependant, pour supprimer tous les fichiers zip par la suite, je dois faire

rm *.zip

Autrement dit, il ne veut pas les guillemets. La décompression, en revanche, ne fonctionne pas si je lui donne simplement le * (me prévient que "les fichiers ne correspondent pas").

Pourquoi est-ce différent? Pour moi, cela semble être exactement la même opération. Ou est-ce que j'utilise mal le joker?

Les introductions à la wildcard dans Unix n'entrent pas vraiment dans cela, et je ne pouvais rien localiser dans la documentation rmou zip.

J'utilise le terminal sur un Mac (Yosemite).

Patrick
la source
4
Je n'avais aucune idée que unzipje pouvais le faire sans la for f in *.zip;do...doneboucle de shell normale . Une interface utilisateur en ligne de commande aussi étrange que celle de type Unix.
Peter Cordes
@ Peter Je pense que vous comprenez mal la situation. unzipapplique le glob au contenu d'une archive; vous ne pouvez pas les obtenir de bash avec un joker. (Vous auriez besoin de `` `` pour le f in unzip -l archive.zip; do ... done`)
alexis
@ alexis: Je savais qu'il était préférable d' unzipaccepter les globs pour correspondre dans un fichier zip unique. Mais c'est différent. J'ai effectivement essayé unzip '*.zip'dans un répertoire avec plusieurs fichiers zip, et il extrait tous les fichiers de tous les zips. Comme je le disais, super-bizarre. tarn'a pas de mode de fonctionnement comme ça.
Peter Cordes
1
@ Peter, je vois ... oui, c'est bizarre, d'autant plus que décompresser n'acceptera pas plusieurs arguments en ligne de commande! Clairement une implémentation Windows uniquement. J'ai mal interprété la description de la tâche par le PO.
Alexis
1
@ alexis: PKZip est antérieure à Windows . C'est un programme en ligne de commande DOS, publié pour la première fois en 1989. Le port Unix utilise essentiellement le même code d'analyse de lignes de commande, AFAIK.
Peter Cordes

Réponses:

68

Vous avez très bien expliqué la situation. La dernière pièce du puzzle est qu'il unzippeut gérer les caractères génériques lui-même:

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

ARGUMENTS

fichier [.zip]

...

Les expressions génériques sont similaires à celles prises en charge dans les shells Unix couramment utilisés (sh, ksh, csh) et peuvent contenir:

* correspond à une séquence de 0 caractères ou plus

En citant le caractère générique *, vous avez empêché votre shell de l’étendre, ce qui permet de unzipvoir le caractère générique et de l’agrandir selon sa propre logique.

rm, En revanche, ne supporte pas les caractères génériques sur son propre , afin de tenter de citer un caractère générique ordonnera rmà la recherche d'un astérisque littéral dans le nom du fichier à la place.

La raison pour laquelle cela unzip *.zipne fonctionne pas est que sa unzipsyntaxe ne permet tout simplement pas plusieurs fichiers zip; s'il y a plusieurs paramètres, il s'attend à ce que le deuxième et les suivants soient des fichiers dans l'archive:

décompressez [-Z] [-cflptTuvz [abjnoqsCDKLMUVWX $ /: ^]] le fichier [.zip] [fichier (s) ...] [-x xfile (s) ...] [-d exdir]

Jeff Schaller
la source
6
merci, c'est logique! si je comprends bien, dans un cas, je parle sa unzippropre langue, dans l’autre, le jargon général Unix?
Patrick
6
Correct. Il est important de garder à l'esprit ce que votre shell fait par rapport à ce qu'un programme fait.
Jeff Schaller
7
pkzip a été créé sous DOS sans élargir les caractères génériques transmis aux programmes.
Thorbjørn Ravn Andersen
11
@patrick la manière unix de traiter plusieurs fichiers avec un programme qui ne peut fonctionner qu'avec un seul fichier à la fois est d'utiliser une boucle. par exemple for f in *.zip ; do unzip -v "$f" ; done. et une grande partie de la raison pour laquelle le shell effectue l’extension de nom de fichier, etc. en soi, est que chaque programme individuel n’a pas à le faire (ce qui entraînerait de nombreuses implémentations écrites indépendamment de l’expansion de caractères génériques qui différaient de manière petite mais ennuyeuse) .
cas
25

La différence entre ces deux commandes est le *caractère cité . Si vous appelez une commande dans un shell et utilisez le *caractère comme argument, le shell lui-même évaluera l'argument. Voir cet exemple:

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

Maintenant avec un *:

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

Le shell évalue le caractère générique et construit une commande comme suit:

$ ls file1.zip  file2.zip  file3.zip

Avec un caractère générique cité, il est interprété comme un fichier nommé (littéralement) *.zip:

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

L' unziputilitaire ne peut pas être appelé avec plusieurs fichiers compressés comme arguments. Mais, le développeur a choisi un autre moyen pour cela. De la page de manuel:

fichier [.zip]

[...] Les expressions génériques sont similaires à celles prises en charge par les shells Unix couramment utilisés (sh, ksh, csh) [...] ( assurez-vous de citer tout caractère qui pourrait autrement être interprété ou modifié par le système d'exploitation , en particulier sous Unix et VMS.)

le chaos
la source
Savez-vous pourquoi les auteurs de ont unzipchoisi d'emprunter cette voie plutôt que de permettre plusieurs fichiers compressés comme arguments?
David Etler
@ DavidEtler, je ne sais pas trop.
chaos
1
Je ne peux pas dire pourquoi non plus, @DavidEtler, mais la syntaxe de décompression telle qu'elle est construite accepte les noms de fichiers après le fichier zip qui sont supposés être le contenu de ce fichier. Ce serait ambigu si vous vouliez qu'un deuxième fichier zip soit un paramètre "unzip me" ou un "décompressez ce fichier zip interne de l'archive précédente".
Jeff Schaller
@DavidEtler ne sait pas ce que les développeurs pensaient, mais tout était beaucoup plus lent et plus petit à l'époque. Vous ne traitiez généralement pas plus d’un fichier zip à la fois. Vous aviez des disquettes contenant 90 ou 250 Ko et vous étiez vraiment heureux d’avoir un lecteur de disque de 10 Mo. Les choses ont été comprimées parce qu'elles devaient l'être, pas seulement pour le transport intersystème.
Joe
7

La différence est dans le premier cas le shell lui-même développe le glob:

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

Dans le second cas, l'application elle-même Does Something ™ avec ce caractère littéral:

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

Si elle n'est pas citée, le shell étend d'abord le glob, et la commande sera exécutée avec le résultat étendu.

thrig
la source
2

Une commande recevra les arguments après leur traitement par le shell.

Lors du premier traitement, un shell *sera ajouté par le shell (à la liste des fichiers du répertoire present (pwd) qui correspondent au modèle):

echo *.zip

Va lister tous les .zipfichiers. Mais neecho "*".zip" sera pas .

Lors du premier traitement, une citation "*"ne sera pas développée, elle sera donnée à la commande unzip en tant que paramètre (une fois la citation supprimée). La commande unzip recevra un paramètre de *.zip:

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

C'est la commande unzip qui permet d'étendre le *à la liste des fichiers.


Il est également intéressant de noter que ces deux commandes ne réaliseront pas exactement la même action finale, et qui étend les *modifications:

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

La première commande reçoit un *.zipfichier qu’elle développe pour traiter tous les fichiers. La deuxième commande unziprecevra une liste de tous les .zipfichiers du fichier pwd, qu’elle ne traitera pas, car le développeur décompacté a choisi de rejeter le développement de plusieurs zipfichiers.


la source
0

Les guillemets sont nécessaires en raison de la façon dont zip gère plusieurs arguments:

rm: supprimer tous les fichiers de la liste d'arguments

zip: décompressez le fichier dans le premier argument. extraire uniquement les fichiers dans les arguments restants.

$ 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

comme vous pouvez le voir, il essaie de trouver file2.zip et file3.zip dans file1.zip

afin de vous permettre d'extraire plusieurs fichiers zip à la fois, zip prend en charge l'interprétation du glob par lui-même, avec un résultat différent.

eMBee
la source