Ho una directory che contiene diverse sottodirectory. C'è una domanda sulla compressione dei file che contiene una risposta che ho leggermente modificato per le mie esigenze.
for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done
Tuttavia, mi imbatto in un problema bizzarro. Per il primo insieme di cartelle, dove zips/<name>.zip
non esiste, ottengo questo errore:
zip error: Nothing to do! (zips/2014-10.zip)
zip warning: name not matched: 2014-11/*.csv
comunque quando faccio solo echo
le istruzioni zip:
for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
Quindi esegui il comando echoed (zip zips/2014-10.zip 2014-10/*.csv
), funziona bene e comprime la cartella. Quindi la parte divertente è che le successive esecuzioni del comando originale saranno in realtà comprimere le cartelle che non hanno funzionato la prima volta!
Per testare tu stesso questo comportamento:
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
Vedrai che l'eco stampa queste istruzioni:
zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv
Tuttavia, il comando zip effettivo ti dirà:
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%)
Quindi sta effettivamente aggiornando il file zip con il .csv
s dove esiste il file zip, ma non quando viene creato il file zip. E se copi uno dei comandi zip:
$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)
Quindi esegui nuovamente zip-all-the-things:
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done
Vedrai che si aggiorna per 2016-02
e 2016-03
. Ecco il mio output di 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
Inoltre, (non) sorprendentemente, funziona bene:
zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"
Cosa sto sbagliando qui? (nota, sto usando zsh invece di bash, se questo fa la differenza)
Risposta accettata:
Espansione tramite la shell
Le virgolette intorno a "$i*.csv"
Fai la differenza. Con le virgolette, la shell espande quella stringa in "2014-11/*.csv". Quel file esatto non esiste e zip
segnala un errore. Senza virgolette, il *
si espande anche (tramite espansione del nome file/"globbing") e il risultante zip
comando è un elenco completo di file corrispondenti, ciascuno come argomento separato. Puoi ottenere il secondo comportamento, all'interno di for
ciclo, con:
for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done
Espansione tramite zip
zip
può anche espandere i caratteri jolly per se stesso, ma non in tutte le situazioni. Dal manuale zip:
Il zip il programma può fare la stessa corrispondenza sui nomi che sono in zip archivio in fase di modifica o, nel caso di -x (escludi) o -i (includere) le opzioni, nell'elenco dei file su cui operare, utilizzando barre inverse o virgolette per dire alla shell di non eseguire l'espansione del nome.
Il comando originale funziona nei tentativi successivi, dopo aver creato con successo un archivio, perché zip
tenta di confrontare i caratteri jolly con il contenuto dell'archivio esistente. Esistono lì ed esistono ancora nel filesystem, quindi vengono segnalati con updating:
.
Per ottenere zip
per gestire i caratteri jolly durante la creazione nell'archivio, usa il -r
(ricorso) opzione per ricorrere nella directory richiesta e -i
(includere) per limitarlo ai file che corrispondono al modello:
for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done