Mám adresář, který obsahuje několik podadresářů. Existuje otázka týkající se komprimování souborů, která obsahuje odpověď, kterou jsem vždy tak trochu upravil pro své potřeby.
for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done
Narážím však na bizarní problém. Pro první sadu složek, kde jsou zips/<name>.zip
neexistuje, dostávám tuto chybu:
zip error: Nothing to do! (zips/2014-10.zip)
zip warning: name not matched: 2014-11/*.csv
nicméně když jen echo
příkazy zip:
for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
Poté spusťte příkaz echoed (zip zips/2014-10.zip 2014-10/*.csv
), funguje dobře a složku zapíná na zip. Nejzábavnější na tom je, že následné spuštění původního příkazu bude ve skutečnosti zazipujte složky, které nefungovaly poprvé!
Chcete-li toto chování sami vyzkoušet:
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
Uvidíte, že echo vytiskne tato prohlášení:
zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv
Skutečný příkaz zip vám však řekne:
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%)
Ve skutečnosti se tedy v souboru zip aktualizuje soubor .csv
s kde soubor zip existuje, ale ne při vytvoření souboru zip. A pokud zkopírujete jeden z příkazů zip:
$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)
Poté znovu spusťte soubor zip-all-the-things:
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done
Uvidíte, že se aktualizuje pro 2016-02
a 2016-03
. Zde je můj výstup ze 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
Také to (ne)překvapivě funguje dobře:
zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"
co tady dělám špatně? (Všimněte si, že používám zsh místo bash, pokud to má nějaký rozdíl)
Přijatá odpověď:
Rozšíření pomocí shellu
Uvozovky kolem "$i*.csv"
dělat rozdíl. Pomocí uvozovek shell tento řetězec rozšíří na „2014-11/*.csv“. Tento přesný soubor neexistuje a zip
hlásí chybu. Bez uvozovek, *
také expanduje (prostřednictvím expanze názvu/”globbing”) a výsledného zip
příkaz je úplný seznam odpovídajících souborů, každý jako samostatný argument. Druhé chování můžete získat uvnitř for
smyčka s:
for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done
Rozšíření pomocí zip
zip
může také rozšířit zástupné znaky pro sebe, ale ne ve všech situacích. Z příručky zip:
zip program může provést stejnou shodu se jmény, která jsou v zip archivu, který se upravuje nebo v případě -x (vyloučit) nebo -i (zahrnout) možnosti v seznamu souborů, se kterými se má pracovat, pomocí zpětných lomítek nebo uvozovek, aby shell neprováděl rozšíření názvu.
Původní příkaz funguje na další pokusy poté, co jste úspěšně vytvořili archiv, protože zip
se pokusí porovnat zástupné znaky s obsahem existujícího archivu. Existují tam a stále existují v souborovém systému, takže jsou hlášeny pomocí updating:
.
Chcete-li získat zip
pro zpracování zástupných znaků při vytváření archivu, použijte -r
(recurse) možnost rekurze do požadovaného adresáře a -i
(zahrnout), chcete-li jej omezit na soubory odpovídající vzoru:
for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done