GNU/Linux >> Linux Esercitazione >  >> Linux

Perché `zip` in un ciclo For funziona quando il file esiste, ma non quando non lo è?

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

Linux
  1. Perché l'espressione regolare funziona in X ma non in Y?

  2. Perché la sostituzione del processo Bash non funziona con alcuni comandi?

  3. Il comando Shuf File> File lascia un file vuoto, ma comandi simili no??

  4. Linux:perché Locale Es_mx funziona ma non Es?

  5. Perché il documento Parent Shell Here non funziona per il sottocomando in Dash ma Bash funziona?

Perché il modo seguente non cambia la dimensione del limite del file principale?

Perché il file di traduzione Bash non contiene tutti i testi di errore?

Linux – Perché Setuid non funziona??

Perché Ls -l non mostra l'ora e l'anno per ogni file??

Perché il completamento automatico non funziona quando si digita un nome di comando dopo "source"?

Perché questo "durante la lettura" funziona in un terminale, ma non in uno script di shell?