GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum funktioniert „zip“ in einer For-Schleife, wenn die Datei existiert, aber nicht, wenn sie nicht existiert?

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

Linux
  1. Warum erkennt das Bash-Skript keine Aliase?

  2. Warum funktioniert das ~/.bash_profile nicht?

  3. Linux – Warum funktioniert Setuid nicht?

  4. Core-Dump, aber Core-Datei befindet sich nicht im aktuellen Verzeichnis?

  5. Warum funktioniert Tomcat mit Port 8080, aber nicht mit 80?

Warum zeigt Ls -l nicht die Uhrzeit und/oder das Jahr für jede Datei an?

Warum funktioniert die automatische Vervollständigung nicht, wenn ein Befehlsname nach „Quelle“ eingegeben wird?

Linux – Warum funktioniert Locale Es_mx, aber nicht Es?

Warum funktioniert das Parent Shell Here-Dokument nicht für Unterbefehle in Dash, aber Bash funktioniert?

Warum funktioniert dieses „beim Lesen“ in einem Terminal, aber nicht in einem Shell-Skript?

Warum funktioniert `exit &` nicht?