为什么zip和rm命令之间的通配符*如此不同?


58

我整理了一个脚本来为我执行一些文件操作。我正在使用通配符运算符*将函数应用于一种类型的所有文件,但是我没有得到一件事。我可以将unzip所有文件放在这样的文件夹中

unzip "*".zip

但是,要稍后删除所有zip文件,我需要

rm *.zip

也就是说,它不需要引号。另一方面,如果我只给它*,则解压缩不起作用(警告我“文件不匹配”)。

为什么有什么不同?对我来说,这似乎是完全相同的操作。还是我使用通配符不正确?

Unix中通配符的介绍并没有真正涉及到这一点,而且我在rmzip文档中找不到任何内容。

我在Mac(优胜美地)上使用终端。


4
我不知道unzip如果没有普通的for f in *.zip;do...doneshell循环就可以做到这一点。这样奇怪的非类unix命令行UI。
彼得·科德斯

@Peter我想你误会了情况。unzip将glob应用于存档的内容;您无法使用通配符从bash中获取它们。(您将需要```对于F中unzip -l archive.zip;做... done`)
亚历克西斯

@alexis:我知道unzip在单个zip文件中接受glob进行匹配。但这是不同的。我实际上unzip '*.zip'在一个包含多个zip文件的目录中尝试过,它从所有zip中提取所有文件。就像我说的那样,很奇怪。 tar没有这样的操作模式。
彼得·科德斯

1
@Peter我看...是的,这很奇怪,特别是因为解压缩将不接受多个命令行参数!显然,仅Windows实现。我曲解了OP对任务的描述。
亚历克西斯

1
@alexis:PKZip早于Windows。它是DOS命令行程序,于1989年首次发布。Unix端口使用基本相同的cmdline解析代码AFAIK。
彼得·科德斯

Answers:


69

您已经很好地说明了这种情况。难题的最后一部分是unzip可以处理通配符本身:

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

争论

文件[.zip]

...

通配符表达式与常用的Unix shell(sh,ksh,csh)所支持的表达式相似,并且可能包含:

*匹配0个或多个字符的序列

通过引用*通配符,可以防止外壳扩展它,以便unzip看到该通配符并根据其自己的逻辑进行扩展。

rm相比之下,不支持通配符自身,因此试图引用一个通配符将指示rm来寻找文件名,而不是字面星号。

unzip *.zip不起作用的原因是unzip的语法根本不允许多个zip文件;如果有多个参数,则期望第二个及后续参数是归档中的文件:

解压缩[-Z] [-cflptTuvz [abjnoqsCDKLMUVWX $ /:^]]文件[.zip] [文件...] [-x xfile ...] [-d exdir]


6
谢谢,这很有意义!如果我理解正确,在一种情况下,我说unzip的是自己的语言,在另一种情况下,我是通用的UNIX语言?
帕特里克

6
正确。重要的是要牢记您的外壳程序与程序的功能。
杰夫·谢勒

7
pkzip起源于DOS,它没有扩展传递给程序的通配符。
托尔比约恩Ravn的安徒生

11
@patrick使用只能同时处理一个文件的程序处理多个文件的unix方法是使用循环。例如for f in *.zip ; do unzip -v "$f" ; done。这也是为什么shell进行文件名扩展等原因的很大一部分,以至于每个程序都不必这样做(这将导致许多独立编写的通配符扩展实现,这些实现的差异很小但很烦人) 。
cas

24

这两个命令之间的区别是带引号的*字符。如果在外壳程序中调用命令并将*字符用作参数,则外壳程序本身将评估该参数。请参阅以下示例:

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

现在带有*

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

外壳程序评估通配符并按如下所示构建命令:

$ ls file1.zip  file2.zip  file3.zip

使用带引号的通配符,它​​将被解释为名为(字面意义上)的文件*.zip

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

unzip不能使用多个压缩文件作为参数来调用该实用程序。但是,开发人员为此选择了另一种方法。从联机帮助页:

文件[.zip]

[...]通配符表达式类似于那些在通常使用的Unix外壳支持(SH,KSH,CSH)[...](请务必注明,否则可能会由操作系统进行解释或修改任何角色,特别是在Unix和VMS。)


您知道为什么作者unzip选择了这种方式,而不是允许多个压缩文件作为参数吗?
David Etler '16

@DavidEtler我也不知道。
混乱

1
我不能说为什么也要使用@DavidEtler,但是在构建时,unzip的语法接受zipfile之后的文件名,这些文件名被假定为该zipfile的内容。您是要让第二个zip文件成为“ unzip me”参数,还是要“从先前的存档中解压缩此内部zip文件”,这将是模棱两可的。
杰夫·谢勒

@DavidEtler不知道开发商在想,但一切都慢和更小的当年。通常您一次不会处理多个zip文件。您的软盘容量为90或250kB,您非常高兴拥有10MB的磁盘驱动器。事物被压缩是因为它们必须被压缩,而不仅仅是为了系统间的传输。

6

区别在于,在第一种情况下,外壳本身扩展了glob:

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

在第二种情况下,应用程序本身使用该文字字符来做Something™:

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

如果不加引号,则外壳程序首先扩展该glob,然后使用该外壳glob扩展到的任何内容运行该命令。


2

在外壳程序处理完参数后,命令将接收这些参数。

在第一次处理时,*shell会将unquoted 展开(到与该模式匹配的当前目录(pwd)中的文件列表):

echo *.zip

将列出所有.zip文件。但是echo "*".zip"不会

第一次处理时,引号"*"将不会被扩展,它将作为参数提供给unzip命令(在删除引号之后)。解压缩命令将接收参数*.zip

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

这是命令unzip,可将展开*到文件列表。


有趣的是,这两个命令将不会执行完全相同的最终操作,而是由谁来扩展*更改:

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

第一个命令会收到一个*.zip,它将扩展为处理所有文件。第二个命令unzip将收到.zippwd 中所有文件的列表,它将不会处理,因为解压缩开发人员已选择拒绝扩展多个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

如您所见,它尝试在file1.zip中找到file2.zip和file3.zip

为了允许您一次提取多个zip文件,zip支持本身解释glob,结果不同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.