J'ai un répertoire qui contient plusieurs sous-répertoires. Il y a une question sur la compression des fichiers qui contient une réponse que j'ai légèrement modifiée pour mes besoins.
for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done
Cependant, je me heurte à un problème bizarre. Pour le premier ensemble de dossiers, où zips/<name>.zip
n'existe pas, j'obtiens cette erreur :
zip error: Nothing to do! (zips/2014-10.zip)
zip warning: name not matched: 2014-11/*.csv
cependant quand je ne fais que echo
les instructions zip :
for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
Exécutez ensuite la commande echoed (zip zips/2014-10.zip 2014-10/*.csv
), cela fonctionne bien et ferme le dossier. Ensuite, la partie amusante à ce sujet est que les exécutions ultérieures de la commande d'origine seront en fait, compressez les dossiers qui n'ont pas fonctionné la première fois !
Pour tester ce comportement vous-même :
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
Vous verrez que l'écho imprime ces déclarations :
zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv
Cependant, la commande zip réelle vous dira :
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%)
Il s'agit donc en fait de mettre à jour le fichier zip avec le .csv
s où le fichier zip existe, mais pas lors de la création du fichier zip. Et si vous copiez une des commandes zip :
$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)
Ensuite, relancez le zip-all-the-things :
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done
Vous verrez qu'il est mis à jour pour 2016-02
et 2016-03
. Voici ma sortie de 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
De plus, (sans) surprise, cela fonctionne très bien :
zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"
Qu'est-ce que je fais de mal ici ? (remarque, j'utilise zsh au lieu de bash, si cela fait une différence)
Réponse acceptée :
Expansion par le shell
Les guillemets autour de "$i*.csv"
Fais la différence. Avec les guillemets, le shell développe cette chaîne en "2014-11/*.csv". Ce fichier exact n'existe pas, et zip
signale une erreur. Sans guillemets, le *
se développe également (via l'expansion du nom de fichier/”globbing”), et le résultat zip
La commande est une liste complète des fichiers correspondants, chacun sous la forme d'un argument distinct. Vous pouvez obtenir le deuxième comportement, à l'intérieur du for
boucle, avec :
for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done
Extension par zip
zip
peut également étendre les caractères génériques pour lui-même, mais pas dans toutes les situations. À partir du manuel zip :
Le zip le programme peut faire la même correspondance sur les noms qui sont dans le zip archive en cours de modification ou, dans le cas de -x (exclure) ou -i (inclure) les options, sur la liste des fichiers à traiter, en utilisant des barres obliques inverses ou des guillemets pour indiquer au shell de ne pas développer le nom.
La commande d'origine fonctionne lors des tentatives suivantes, une fois que vous avez créé une archive avec succès, car zip
essaie de faire correspondre les caractères génériques avec le contenu de l'archive existante. Ils existent là-bas et existent toujours sur le système de fichiers, ils sont donc signalés avec updating:
.
Pour obtenir le zip
pour gérer les caractères génériques lors de la création l'archive, utilisez le -r
(recurse) option pour revenir dans le répertoire demandé, et -i
(inclure) pour le limiter aux fichiers correspondant au modèle :
for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done