Почему `zip` в цикле for работает, когда файл существует, но не тогда, когда он этого не делает?

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

for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done 

Однако я столкнулся с причудливой проблемой. Для первого набора папок, где zips/<name>.zip не существует, я получаю эту ошибку:

 zip error: Nothing to do! (zips/2014-10.zip) zip warning: name not matched: 2014-11/*.csv - zip error: Nothing to do! (zips/2014-10.zip) zip warning: name not matched: 2014-11/*.csv 

однако, когда я просто echo заявления zip:

 for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done 

Затем запустите эхо-команду ( zip zips/2014-10.zip 2014-10/*.csv ), она отлично работает и застегивает папку вверх. Тогда интересная часть об этом заключается в том, что последующие прогоны исходной команды будут фактически застегивать папки, которые не срабатывали в первый раз!

Чтобы проверить это поведение самостоятельно:

 cd /tmp mkdir -p 2016-01 2016-02 2016-03 zips for i in 2*/; do touch "$i"/one.csv; done for i in 2*/; do touch "$i"/two.csv; done zip zips/2016-03.zip 2016-03/*.csv for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done 

Вы увидите, что эхо выводит эти утверждения:

 zip zips/2016-01.zip 2016-01/*.csv zip zips/2016-02.zip 2016-02/*.csv zip zips/2016-03.zip 2016-03/*.csv 

Однако, настоящая команда zip сообщит вам:

  zip warning: name not matched: 2016-01/*.csv zip error: Nothing to do! (zips/2016-01.zip) zip warning: name not matched: 2016-02/*.csv zip error: Nothing to do! (zips/2016-02.zip) updating: 2016-03/one.csv (stored 0%) updating: 2016-03/two.csv (stored 0%) -  zip warning: name not matched: 2016-01/*.csv zip error: Nothing to do! (zips/2016-01.zip) zip warning: name not matched: 2016-02/*.csv zip error: Nothing to do! (zips/2016-02.zip) updating: 2016-03/one.csv (stored 0%) updating: 2016-03/two.csv (stored 0%) -  zip warning: name not matched: 2016-01/*.csv zip error: Nothing to do! (zips/2016-01.zip) zip warning: name not matched: 2016-02/*.csv zip error: Nothing to do! (zips/2016-02.zip) updating: 2016-03/one.csv (stored 0%) updating: 2016-03/two.csv (stored 0%) 

Поэтому он фактически обновляет zip-файл с помощью .csv s, где существует zip-файл, но не тогда, когда создается zip-файл. И если вы скопируете одну из команд zip:

 $ zip zips/2016-02.zip 2016-02/*.csv adding: 2016-02/one.csv (stored 0%) adding: 2016-02/two.csv (stored 0%) 

Затем заново запустите zip-all-the-things:

 for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done 

Вы увидите, что он обновляется для 2016-02 и 2016-03 . Вот мой вывод tree :

 . ├── 2016-01 │  ├── one.csv │  └── two.csv ├── 2016-02 │  ├── one.csv │  └── two.csv ├── 2016-03 │  ├── one.csv │  └── two.csv └── zips ├── 2016-02.zip └── 2016-03.zip 

Кроме того, (un) удивительно, это работает отлично:

 zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)" 

Что я здесь делаю неправильно? (обратите внимание, я использую zsh вместо bash, если это имеет значение)

One Solution collect form web for “Почему `zip` в цикле for работает, когда файл существует, но не тогда, когда он этого не делает?”

Расширение оболочки

Котировки вокруг "$i*.csv" делают разницу. С помощью кавычек оболочка расширяет эту строку до «2014-11 / *. Csv». Точного файла не существует, и zip сообщает об ошибке. Без кавычек * также расширяется (посредством расширения имени файла / «globbing»), а итоговая zip команда представляет собой полный список совпадающих файлов, каждый из которых является отдельным аргументом. Вы можете получить второе поведение внутри цикла for :

 for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done 

Расширение по zip

zip также может расширять подстановочные знаки для себя, но не во всех ситуациях. Из руководства по zip :

Программа zip может выполнять одно и то же сопоставление по именам, которые находятся в модифицированном zip- архиве, или в случае опций -x (exclude) или -i (include) в списке файлов, которые будут использоваться, используя обратную косую черту или кавычки, чтобы сообщить оболочке не расширять имя.

Исходная команда работает при последующих попытках после успешного создания архива, поскольку zip пытается сопоставить подстановочные знаки с содержимым существующего архива. Они существуют и все еще существуют в файловой системе, поэтому они сообщаются с updating:

Чтобы получить zip для обработки подстановочных знаков при создании архива, используйте параметр -r (recurse) для рекурсии в запрошенный каталог и -i (include), чтобы ограничить его файлами, соответствующими шаблону:

  for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done 
  • zle читать первое слово в строке?
  • Заполнение вкладки «../» в zsh
  • Zsh auto complete предлагает полные функции (_git _vim и т. Д.)
  • Byobu - Новое окно с той же сессией ssh?
  • Сортировка строк с помощью escape-кодов ANSI
  • Переименование файлов в лексикографическом порядке с помощью числового шаблона, который последовательно увеличивается с фиксированным заполнением
  • Разделить оболочку горизонтально, чтобы показать ls -al и pwd
  • Стандартный / канонический способ проверить, выводит ли предыдущий трубопровод выпуск?
  • Передача именованных аргументов в сценарии оболочки
  • Пробелы как разрывы строк из команды inline for loop
  • Команда Wrap в подсказке
  • Linux и Unix - лучшая ОС в мире.