gzip alle Dateien mit bestimmten Erweiterungen

11

Ich versuche, alle Dateien auf Ubuntu mit der Dateierweiterung .css, .html oder .js zu komprimieren. in einem Top-Verzeichnis und allen Unterverzeichnissen. Ich möchte die Originaldateien behalten und die GZ-Datei überschreiben, falls bereits vorhanden.

Wenn ich also n Dateien habe, möchte ich diese n Dateien behalten und zusätzliche n Archivdateien erstellen. Nicht nur einer.

Mein Versuch war, ein Skript auszuführen, das so aussieht:

gzip -rkf *.css
gzip -rkf *.html
... one line for each file extension

Erstens: Ich muss eine Zeile in diesem Skript für jede Dateierweiterung haben, die ich gzipen möchte. Das ist in Ordnung, aber ich hoffe, einen besseren Weg zu finden

Zweitens und wichtiger: Es funktioniert nicht. Obwohl -r die Aufgabe übernehmen sollte, bleiben die Unterverzeichnisse unverändert. Die gzip-Datei wird nur im obersten Verzeichnis erstellt.

Was fehlt mir hier?

Übrigens: Das Folgende ist ein Fehler in der ausführlichen Ausgabe, richtig? Bei Verwendung der Optionen -k und -v

-k, --keep        keep (don't delete) input files
-v, --verbose     verbose mode

Die ausführliche Ausgabe besagt, dass die Datei ersetzt wird, obwohl "Ersetzen" bedeutet, dass die Originaldatei nach dem Ersetzen nicht vorhanden ist. Auf jeden Fall ist dies nur die Ausgabesache.

$ ls
  index.html      subdir1  testfile      testfile.css.gz
  javaclass.java  subdir2  testfile.css
$ gzip -fkv *.css
  testfile.css:   6.6% -- replaced with testfile.css.gz
$ ls
  index.html      subdir1  testfile      testfile.css.gz
  javaclass.java  subdir2  testfile.css
Sadik
quelle
1
-rfunktioniert wie geplant. Von man gzip : Durchlaufen Sie die Verzeichnisstruktur rekursiv. Wenn einer der in der Befehlszeile angegebenen Dateinamen Verzeichnisse sind , steigt gzip in das Verzeichnis ab und komprimiert alle dort gefundenen Dateien (oder dekomprimiert sie im Fall von gunzip). (Hervorhebung von mir)
Dennis
In Ordnung. Also würde -r ein Verzeichnis mit dem Namen XYZ.css eingeben. Dann ist die Rekursion nicht so gestaltet, wie ich es erwartet habe.
Sadik

Antworten:

7

Sie können dies mit einer for-Schleife tun, um jede Datei zu finden und dann zu komprimieren:

for i in `find | grep -E "\.css$|\.html$"`; do gzip "$i" ; done
mndo
quelle
Vielen Dank! Obwohl die -rOption nicht funktioniert -kund -ffunktioniert, kann ich sie folgendermaßen verwenden: for i in find | grep -E "\.css$|\.html$"; mache gzip -vkf "$ i"; erledigt`
Sadik
@ Sadik: Sei vorsichtig! Dieser Ansatz funktioniert nicht, wenn einer der Dateinamen ein Leerzeichen enthält.
Dennis
Können Sie erklären, warum nicht?
Sadik
1
@Sadik: Stellt `...`eine Zeichenfolge bereit, keine Liste. forverwendet das interne Feldtrennzeichen ( $IFS), um zu entscheiden, wo diese Zeichenfolge aufgeteilt werden soll. Standardmäßig werden Zeilenvorschübe, Tabulatoren und Leerzeichen aufgeteilt. Wenn Sie also eine Datei mit dem Namen haben new style.css, werden die Befehle gzip newund gzip style.cssausgeführt.
Dennis
1
@ Sadik, Dennis hat recht, als schnelle Problemumgehung können Sie export IFS=$'\n'kurz vor der forSchleife ausführen .
mndo
14

ich würde ... benutzen

find /path/to/dir \( -name '*.css' -o -name '*.html' \) -exec gzip --verbose --keep {} \;

Wechseln Sie namezu, inamewenn Sie die Erweiterungen ohne Berücksichtigung der Groß- und Kleinschreibung abgleichen möchten (dh Include .CSSund / oder .HTMLErweiterungen). Sie können das weglassen, /path/to/dirwenn Sie die rekursive Suche aus dem aktuellen Verzeichnis starten möchten.

Steeldriver
quelle
2
Wenn Sie sich über den --keepWechsel wundern , werden die Originaldateien beibehalten. Lassen Sie es weg, wenn Sie möchten, dass sie nach dem Komprimieren gelöscht werden.
Ben Johnson
4

So erhalten Sie die Liste der Dateien:

find -type f | grep -P '\.js|\.html|\.css'

Und um all diese Dateien zu gzipen:

find -type f | grep -P '\.js|\.html|\.css' | tar cvzf archive.gz -T -
Chaos
quelle
Wäre dies nicht tardie Liste der Dateien, die von ausgegeben werden find, und nicht die Dateien selbst?
Jos
Ich habe meine Frage bearbeitet, um zu verdeutlichen, dass ich für jede CSS-, HTML- oder JS-Datei eine Archivdatei haben möchte.
Sadik
2
@Jos no mit der -TOption tarverarbeitet die Eingabe als Dateinamen.
Chaos
@chaos Ah, danke. Ich habe heute etwas gelernt.
Jos
2

Ich habe die Antwort von steeldriver verwendet , aber ich möchte sie mit den Optionen --bestund vervollständigen --force.

cdin einen beliebigen Ordner und geben Sie diesen Code ein. Alle Ihre passenden Dateien werden komprimiert.

find . \( -name '*.css' -o -name '*.js' \) -exec gzip --verbose --keep --best --force {} \;
  • Verwenden Sie --bestfür das beste Kompressionsverhältnis.
  • Verwenden Sie --forcezum Überschreiben , ohne zu fragen , ob es bereits eine gzip - Datei ist.
Azerafati
quelle
1

Sie können Globstar verwenden.

Wenn die globstarShell-Option aktiviert ist, benötigen Sie lediglich gzip -vk **/*.{css,html}.

Die Bash - Shell hat eine globstarOption , die Sie schreiben rekursive lässt Klackse mit **. shopt -s globstaraktiviert es. Möglicherweise möchten Sie dies jedoch nicht für andere Befehle tun, die Sie später ausführen, sodass Sie es und Ihren gzip Befehl stattdessen in einer Subshell ausführen können .

Dieser Befehl enthält gzipalle .cssund .htmlDateien im aktuellen Verzeichnis in einem seiner Unterverzeichnisse, in einem ihrer Unterverzeichnisse usw., wobei die Originaldateien ( -k) beibehalten und Ihnen mitgeteilt werden, was sie tun ( -v):

(shopt -s globstar; gzip -vk **/*.{css,html})

Wenn Sie Dateinamen ohne Berücksichtigung der Groß- und Kleinschreibung abgleichen möchten, damit diese Erweiterungen mit einigen oder allen Großbuchstaben enthalten sind, können Sie auch die nocaseglobShell-Option aktivieren :

(shopt -s globstar nocaseglob; gzip -vk **/*.{css,html})

;trennt die beiden Befehle und die äußere ( )bewirkt, dass sie in einer Subshell ausgeführt werden. Das Festlegen einer Shell-Option in einer Subshell führt nicht dazu, dass sie in der aufrufenden Shell festgelegt wird. Wenn Sie es wollen , aktivieren , globstardann können Sie laufen shopt -s globstar; dann können Sie einfach den Befehl ausführen:

gzip -vk **/*.{css,html}

Sie können deaktivieren globstarmit shopt -u globstar. Sie können überprüfen, ob es derzeit mit aktiviert ist shopt globstar.

Wie es funktioniert

Der Schlüssel zur Funktionsweise dieses gzipBefehls besteht darin, dass die Shell Erweiterungen ausführt, um eine Liste aller Dateien in der Verzeichnishierarchie mit einem übereinstimmenden Namen zu erstellen und dann jeden dieser Dateinamen als Argumente an zu übergeben gzip.

  • Die Klammererweiterung wird **/*.{css,html}zu **/*.css **/*.html.
  • Dann erweitert globbing diese beiden Muster in die Namen von Dateien, auf die unter dem aktuellen Verzeichnis zugegriffen werden kann ( **aufgrund von globstar), deren Dateinamen aus irgendetwas ( *) bestehen, gefolgt vom angegebenen Suffix ( .cssoder .htmlin diesem Fall).

Dies stimmt nicht mit Dateien überein, deren Namen mit beginnen. oder die sich in Verzeichnissen befinden, die auf diese Weise benannt wurden. Sie haben wahrscheinlich keine solchen HTML- und CSS-Dateien, und wenn Sie dies tun, möchten Sie sie wahrscheinlich nicht einschließen. Wenn Sie sie jedoch einschließen möchten, können Sie sie je nach Ihren Anforderungen explizit abgleichen. Zum Beispiel, Ändern **/*.{css,html}zu **/{,.}*.{css,html}gehören Dateien , die mit beginnen , .während noch nicht in Ordnern zu suchen , die zu tun.

Wenn Sie möchten, dass sowohl Dateien, deren Namen beginnen, .als auch Dateien in Verzeichnissen, deren Namen beginnen ., eingeschlossen werden, gibt es eine sauberere und einfachere Möglichkeit: Aktivieren Sie die dotglobShell-Option.

(shopt -s globstar dotglob; gzip -vk **/*.{css,html})

Oder wenn Sie zwischen Groß- und Kleinschreibung unterscheiden und Dateinamen abgleichen möchten , die beginnen mit .:

(shopt -s globstar nocaseglob dotglob; gzip -vk **/*.{css,html})

Es ist möglich, wenn auch sehr selten, **sich auf etwas zu Langes auszudehnen.

Wenn Sie eine große Anzahl von Dateien haben, die auf diese Weise benannt wurden, schlägt dies möglicherweise mit einer Fehlermeldung fehl, die erklärt, dass die Shell die Befehlszeile nicht erstellen kann, weil sie zu lang wäre. (Selbst bei Tausenden von Dateien ist dies normalerweise kein Problem.)

gzip wird überhaupt nicht angerufen, so dass Sie keine halbfertige Arbeit bekommen.

Wenn dieser Fehler auftritt oder wenn Sie sich darüber Sorgen machen, können Sie findmit verwenden -exec, entweder wie steeldriver beschreibt (mit {} \;) oder wie ich unten beschreibe (mit {} +).

Sie können findmit der -execAktion und +für die Effizienz verwenden.

Der gzipBefehl unterstützt die Angabe von Namen mehrerer zu komprimierender Dateien. Dieser findBefehl funktioniert zwar gut und ist nur dann langsam, wenn Sie über viele Dateien verfügen . Er führt den gzipBefehl jedoch einmal für jede Datei aus:

find . \( -name \*.css -o -name \*.html \) -exec gzip -vk {} \;

Dies funktioniert und Sie können es definitiv verwenden. ( .Sucht aus dem aktuellen Verzeichnis. Außerdem ist es eine etwas andere Art, den Befehl in die sehr gute Antwort von steeldriver zu schreiben . Sie können den von Ihnen bevorzugten Stil verwenden.)

Sie können auch findmehrere Dateinamen übergeben gzipund nur so oft wie nötig ausführen - was fast immer nur einmal der Fall ist. Um dies zu erreichen , verwenden +statt\; . Das +Argument sollte gleich danach kommen {}. findwird +durch zusätzliche Dateinamen ersetzt, falls vorhanden.

find . \( -name \*.css -o -name \*.html \) -exec gzip -vk {} +

Die Verwendung ist in Ordnung, +auch wenn nur wenige übereinstimmende Dateien vorhanden sind. Wenn viele vorhanden sind, kann dies spürbar schneller sein als ein separater gzipAufruf für jede Datei.

Wie steeldriver erwähnt , können Sie -inameanstelle von -nameDateien abgleichen , deren Name wie .cssoder .htmlaber mit unterschiedlicher Groß- und Kleinschreibung endet . Dies entspricht dem Aktivieren nocaseglobin dem globstaroben beschriebenen basierenden Verfahren.

Schließlich haben Sie wahrscheinlich keine passenden Dateien oder Verzeichnisse, die mit beginnen .. Wenn Sie dies jedoch tun, werden sie findautomatisch eingeschlossen. Wenn Sie sie ausschließen möchten (wie dies bei der globstaroben beschriebenen Methode der Fall ist, wenn sie deaktiviert dotglobist), können Sie :

find . -not -path '*/.*' \( -name \*.css -o -name \*.html \) -exec gzip -vk {} +

Die globstaroben beschriebene Methode ist einfacher zu schreiben, insbesondere wenn Sie Verzeichnisse und Dateien ausschließen, die mit beginnen ., da dies die Standardeinstellung ist.

Was nicht zu tun ...

Dateinamen können beliebige Zeichen außer dem Pfadtrennzeichen /und dem Nullzeichen enthalten . Es gibt viele Techniken, die bei seltsamen Dateinamen brechen, und sie sind normalerweise komplizierter als Techniken, die immer nur funktionieren. Ich schlage daher vor, sie zu vermeiden, selbst wenn Sie wissen (oder glauben, dass Sie es wissen), dass sie in Ihrer spezifischen Situation in Ordnung sind. Und natürlich Sie müssen sie nicht verwenden , wenn Sie Dateinamen mit Zeichen haben könnten , die speziell behandelt werden können, einschließlich Leerzeichen.

Es ist möglich, die Ausgabe sicher findan einen anderen Befehl weiterzuleiten , der sie verarbeitet, wenn Sie -print0eine ähnliche Aktion verwenden, um ein Nullzeichen zwischen Pfaden anstelle einer neuen Zeile zu platzieren , und nicht anders. Dateinamen können Zeilenumbrüche enthalten (obwohl ich Sie davon abhalte, absichtlich Dateien mit ihnen zu benennen). Ein findBefehl mit der -printAktion - einschließlich Suchbefehlen ohne explizite Aktion, da dies -printdie Standardeinstellung ist - erzeugt keine Ausgabe, die sicher an einen anderen Befehl weitergeleitet oder anderweitig bereitgestellt werden kann, der eine Aktion für die Dateien ausführt.

Die findmit der -print0Aktion erzeugte Ausgabe kann sicher an weitergeleitet werden xargs -0(das -0Flag weist darauf hin xargs, dass eine durch Null getrennte Eingabe zu erwarten ist).

Eliah Kagan
quelle
0

So rekursieren Sie alle Dateien in einem Ordner / Unterordner rekursiv:

gzip -r `find . -type f -name "*.html"` 

Entpacken:

gunzip -r `find . -type f -name "*.gz"` 
Naruto_Hokage
quelle
Diese auf Befehlssubstitution basierende Methode wird häufig unterbrochen und ist ziemlich schlecht. Das Problem ist, dass Dateinamen, die Leerzeichen oder andere Leerzeichen enthalten, aufgeteilt und als mehrere Dateinamen behandelt werden. (Diese Befehle werden mit ` `Syntax geschrieben, aber das Problem tritt auch bei Verwendung der $( )Syntax vollständig auf .)
Eliah Kagan