Почему `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 
  • как прокручивать несколько строк в завершении экрана zsh
  • Выберите пользовательский запрос zsh на основе того, какой эмулятор я использую
  • Перестроить индекс автозаполнения (или как он там называется) и двоичные файлы в кеше $ PATH в zsh
  • В zsh как я могу перечислить все переменные среды?
  • привязка ключевых ссылок к функциям оболочки в zsh
  • Приоритет && vs & in bash и zsh
  • Портативный способ получить абсолютный путь скрипта?
  • Можно ли в реальном времени заменить текст в оболочке?
  • Как вы управляете своими псевдонимами bash / zsh?
  • Используйте `/ etc / paths` или` / etc / paths.d` для добавления элементов в PATH в macOS Sierra?
  • Измените zsh-завершение, чтобы выполнить доработки над подсказкой
  • Linux и Unix - лучшая ОС в мире.