GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi "zip" dans une boucle For fonctionne-t-il lorsque le fichier existe, mais pas lorsqu'il n'existe pas ?

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 :

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

Linux
  1. Pourquoi la substitution de processus Bash ne fonctionne-t-elle pas avec certaines commandes ?

  2. Pourquoi la méthode suivante ne modifie-t-elle pas la taille limite du fichier principal ?

  3. Pourquoi le fichier de traduction Bash ne contient-il pas tous les textes d'erreur ?

  4. Core dumpé, mais le fichier core n'est pas dans le répertoire courant ?

  5. Pourquoi Tomcat fonctionne-t-il avec le port 8080 mais pas 80 ?

Linux – Pourquoi la locale Es_mx fonctionne-t-elle mais pas Es ?

Pourquoi le parent Shell Here-document ne fonctionne pas pour la sous-commande dans Dash mais Bash fonctionne?

Pourquoi ce "pendant la lecture" fonctionne-t-il dans un terminal, mais pas dans un script shell ?

Pourquoi `exit &` ne fonctionne pas ?

Pourquoi rsync n'utilise-t-il pas delta-transfer pour les fichiers locaux ?

Pourquoi wget'ing une image me donne-t-il un fichier, pas une image ?