Почему подстановочный знак * отличается между командами zip и rm?

58

Я собрал скрипт, чтобы сделать некоторые файловые операции для меня. Я использую подстановочный оператор *для применения функций ко всем файлам типа, но есть одна вещь, которую я не понимаю. Я могу unzipвсе файлы в папке, как это

unzip "*".zip

Тем не менее, чтобы удалить все почтовые файлы впоследствии, мне нужно сделать

rm *.zip

То есть он не хочет кавычек. Разархивирование, с другой стороны, не работает, если я просто даю ему * (предупреждает, что «файлы не были сопоставлены»).

Почему это отличается? Мне кажется, это точно такая же операция. Или я использую подстановочный знак неправильно?

Знакомство с подстановочным знаком в Unix на самом деле не относится к этому, и я не смог найти что-либо в rmили zipдокументах.

Я использую терминал на Mac (Yosemite).

Патрик
источник
4
Я понятия не имел, unzipмог бы сделать это без обычного for f in *.zip;do...doneцикла оболочки. Такой странный не-Unix-подобный интерфейс командной строки.
Питер Кордес
@ Питер, я думаю, ты неправильно понял ситуацию. unzipприменяет глобус к содержимому архива; вы не можете получить их из Bash с подстановочным знаком. (Вам нужно `` для f in unzip -l archive.zip; do ... done`)
alexis
@alexis: я знал о том, unzipчто глобусы должны совпадать внутри одного zip-файла. Но это другое; Я на самом деле пытался unzip '*.zip'в каталоге с несколькими файлами ZIP, и он извлекает все файлы из всех ZIP. Как я уже сказал, супер странно. tarне имеет никакого режима работы, подобного этому.
Питер Кордес
1
@ Питер, я вижу ... да, это странно, тем более что unzip не будет принимать несколько аргументов командной строки! Очевидно, реализация только для Windows. Я неверно истолковал описание задачи ОП.
Алексис
1
@alexis: PKZip предшествует Windows . Это программа командной строки DOS, впервые выпущенная в 1989 году. Порт Unix использует в основном тот же код разбора cmdline, AFAIK.
Питер Кордес

Ответы:

68

Вы очень хорошо объяснили ситуацию. Последняя часть головоломки состоит в том, что она unzipможет обрабатывать символы подстановки сама:

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

АРГУМЕНТЫ

[файл .zip]

...

Подстановочные выражения похожи на те, которые поддерживаются в обычно используемых оболочках Unix (sh, ksh, csh) и могут содержать:

* соответствует последовательности из 0 или более символов

Процитировав подстановочный знак *, вы запретили расширению своей оболочки, чтобы он unzipвидел подстановочный знак и имел дело с расширением в соответствии с его собственной логикой.

rm, Напротив, не поддерживает подстановочные знаки на своем собственном , поэтому попытку процитировать подстановочный будет инструктировать rmискать буквальную звездочку в имени файла вместо этого.

Причина, по которой unzip *.zipэто не работает, заключается в том, что unzipсинтаксис просто не позволяет использовать несколько zip-файлов; если имеется несколько параметров, ожидается, что 2-й и последующие параметры будут файлами в архиве:

распаковать [-Z] [-cflptTuvz [abjnoqsCDKLMUVWX $ /: ^]] файл [.zip] [файл (ы) ...] [-x xfile (s) ...] [-d exdir]

Джефф Шаллер
источник
6
спасибо, это имеет смысл! если я правильно понимаю, в одном случае я говорю unzipна своем родном языке, в другом - на общем Unix Lingo?
Патрик
6
Верный. Важно помнить, что делает ваша оболочка, а не программа.
Джефф Шаллер
7
pkzip зародился на DOS, который не расширял шаблоны, передаваемые программам.
Турбьерн Равн Андерсен
11
@patrick Unix способ обработки нескольких файлов с помощью программы, которая может работать только с одним файлом за раз, заключается в использовании цикла. например for f in *.zip ; do unzip -v "$f" ; done. и большая часть причины, почему оболочка выполняет расширение имени файла и т. д. сама по себе такова, что каждая отдельная программа не обязана (что привело бы к множеству независимо написанных реализаций расширения с подстановочными знаками, которые отличались небольшими, но раздражающими способами) ,
Cas
25

Разница между этими двумя командами заключается в кавычках *. Если вы вызываете команду в оболочке и используете *символ для аргумента, оболочка сама оценит аргумент. Смотрите этот пример:

$ 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решили пойти по этому пути, а не использовать несколько сжатых файлов в качестве аргументов?
Дэвид Этлер
@DavidEtler Я тоже не знаю.
хаос
1
Я не могу сказать, почему, @DavidEtler, но как есть, синтаксис unzip принимает имена файлов после zip-файла, которые, как предполагается, являются содержимым этого zip-файла. Было бы неоднозначно, если вы хотите, чтобы второй zip-файл был параметром «unzip me» или «распакуйте этот внутренний zip-файл из предыдущего архива».
Джефф Шаллер
@DavidEtler не знает, о чем думали разработчики, но тогда все было намного медленнее и меньше. Обычно вы не имеете дело с более чем одним zip-файлом одновременно. У вас были дискеты емкостью 90 или 250 КБ, и вы были действительно счастливы иметь 10 МБ дисковод. Вещи были сжаты, потому что они должны были быть, не только для межсистемного транспорта.
Джо
7

Разница в том, что в первом случае оболочка сама расширяет глобус:

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

в то время как во втором случае само приложение делает что-то ™ с этим буквенным символом:

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

Если не заключено в кавычки, оболочка сначала расширяет глоб, и команда будет выполняться с любым расширением глобуса оболочки.

thrig
источник
2

Команда получит аргументы после того, как они были обработаны оболочкой.

При первой обработке не заключенные в кавычки *будут расширяться оболочкой (до списка файлов в текущем каталоге (pwd), которые соответствуют шаблону):

echo *.zip

Перечислю все .zipфайлы. Но неecho "*".zip" буду .

При первой обработке кавычка "*"не будет расширена, она будет передана команде unzip в качестве параметра (после удаления кавычек). Команда unzip получит параметр *.zip:

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

Это команда unzip, которая расширяет *список файлов.


Также интересно, что эти две команды не будут выполнять одно и то же окончательное действие, и кто расширяет *изменения:

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

Первая команда получает *.zipрасширение, которое обрабатывает все файлы. Вторая команда unzipполучит список всех .zipфайлов в pwd, которые она не будет обрабатывать, так как разработчик unzip решил отклонить расширение более чем одного 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

как вы видите, он пытается найти file2.zip и file3.zip внутри file1.zip

чтобы вы могли извлекать сразу несколько zip-файлов, zip поддерживает интерпретацию глобуса самостоятельно, что дает другой результат.

eMBee
источник