Ich habe ein Verzeichnis, das mehrere Unterverzeichnisse enthält. Es gibt eine Frage zum Komprimieren der Dateien, die eine Antwort enthält, die ich ganz leicht für meine Bedürfnisse modifiziert habe.
for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done
Allerdings stoße ich auf ein bizarres Problem. Für die erste Gruppe von Ordnern, wobei zips/<name>.zip
existiert nicht, erhalte ich diesen Fehler:
zip error: Nothing to do! (zips/2014-10.zip)
zip warning: name not matched: 2014-11/*.csv
aber wenn ich nur echo
die Zip-Anweisungen:
for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
Führen Sie dann den echoed-Befehl aus (zip zips/2014-10.zip 2014-10/*.csv
), es funktioniert gut und komprimiert den Ordner. Der lustige Teil daran ist, dass nachfolgende Ausführungen des ursprünglichen Befehls werden tatsächlich Ordner komprimieren, die beim ersten Mal nicht funktionierten!
Um dieses Verhalten selbst zu testen:
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
Sie werden sehen, dass das Echo diese Anweisungen ausgibt:
zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv
Der eigentliche Zip-Befehl sagt Ihnen jedoch:
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%)
Es aktualisiert also tatsächlich die ZIP-Datei mit der .csv
s wo die ZIP-Datei existiert, aber nicht wenn die ZIP-Datei erstellt wird. Und wenn Sie einen der Zip-Befehle kopieren:
$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)
Führen Sie dann die Zip-all-the-things erneut aus:
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done
Sie werden sehen, dass es für 2016-02
aktualisiert wird und 2016-03
. Hier ist meine Ausgabe von 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
Auch das funktioniert (nicht) überraschend gut:
zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"
Was mache ich hier falsch? (Beachten Sie, dass ich zsh anstelle von bash verwende, falls das einen Unterschied macht)
Akzeptierte Antwort:
Erweiterung durch die Shell
Die Anführungszeichen um "$i*.csv"
den Unterschied machen. Mit den Anführungszeichen erweitert die Shell diese Zeichenfolge zu „2014-11/*.csv“. Genau diese Datei existiert nicht, und zip
meldet einen Fehler. Ohne Anführungszeichen wird der *
auch expandiert (über Dateinamenexpansion/”globbing”), und das daraus resultierende zip
Befehl ist eine vollständige Liste übereinstimmender Dateien, jede als separates Argument. Das zweite Verhalten erhalten Sie innerhalb von for
Schleife, mit:
for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done
Erweiterung per Zip
zip
kann auch Platzhalter für sich selbst erweitern, aber nicht in allen Situationen. Aus dem Zip-Handbuch:
Die zip Das Programm kann den gleichen Abgleich mit Namen durchführen, die sich in der ZIP-Datei befinden Archiv geändert wird oder, im Fall von -x (ausschließen) oder -i (include)-Optionen in der Liste der zu bearbeitenden Dateien, indem Sie Backslashes oder Anführungszeichen verwenden, um der Shell mitzuteilen, dass sie die Namenserweiterung nicht durchführen soll.
Der ursprüngliche Befehl funktioniert bei nachfolgenden Versuchen, nachdem Sie erfolgreich ein Archiv erstellt haben, weil zip
versucht, die Platzhalter mit dem Inhalt des vorhandenen Archivs abzugleichen. Sie existieren dort und existieren immer noch auf dem Dateisystem, also werden sie mit updating:
gemeldet .
Um zip
zu erhalten um die Wildcards beim Erstellen zu handhaben das Archiv, verwenden Sie den -r
(recurse) Option, um in das angeforderte Verzeichnis zu rekursieren, und -i
(einschließen), um es auf Dateien zu beschränken, die dem Muster entsprechen:
for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done