gzip wszystkie pliki z określonymi rozszerzeniami

11

Próbuję zgzipować wszystkie pliki na Ubuntu, które mają rozszerzenie .css, .html lub .js. w głównym katalogu i wszystkich podkatalogach. Chcę zachować oryginalne pliki i zastąpić plik .gz, jeśli już istnieje.

Więc kiedy mam n plików, chcę zachować te n plików i utworzyć dodatkowe n plików archiwum. Nie tylko jeden.

Moja próba polegała na uruchomieniu skryptu, który wygląda następująco:

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

Po pierwsze: muszę mieć jedną linię w tym skrypcie dla każdego rozszerzenia pliku, które chcę gzip. W porządku, ale mam nadzieję, że znajdę lepszy sposób

Drugi i ważniejszy: to nie działa. Chociaż -r powinien wykonać zadanie, podkatalogi pozostają niezmienione. Plik gzip jest tworzony tylko w górnym katalogu.

Czego tu brakuje?

Btw: Poniżej znajduje się błąd w pełnych wynikach, prawda? Podczas korzystania z opcji -k i -v

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

Pełne wyjście mówi, że zastępuje plik, chociaż „zamień” oznacza, że ​​oryginalny plik nie istnieje po zamianie. W każdym razie jest to tylko wynik.

$ 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
źródło
1
-rdziała zgodnie z przeznaczeniem. Z man gzip : Rekurencyjnie przeglądaj strukturę katalogów. Jeśli którakolwiek z nazw plików określonych w wierszu poleceń to katalogi , gzip zejdzie do katalogu i skompresuje wszystkie znalezione tam pliki (lub rozpakuje je w przypadku gunzip). (moje podkreślenie)
Dennis,
Ok. Więc -r wprowadziłby katalog o nazwie XYZ.css. Zatem rekursja nie jest zaprojektowana tak, jak się spodziewałem.
Sadik

Odpowiedzi:

7

możesz to zrobić za pomocą pętli for, aby znaleźć każdy plik, a następnie skompresować go:

for i in `find | grep -E "\.css$|\.html$"`; do gzip "$i" ; done
mndo
źródło
Dziękuję Ci! Chociaż -ropcja nie działa -ki -fdziała, więc mogę użyć ich w następujący sposób: dla mnie w find | grep -E "\.css$|\.html$"; do gzip -vkf "$ i"; zrobione`
Sadik
@Sadik: Bądź ostrożny! To podejście nie zadziała, jeśli nazwa pliku zawiera spację.
Dennis
Czy możesz wyjaśnić, dlaczego nie?
Sadik,
1
@Sadik: `...`zapewnia ciąg, a nie listę. forużywa wewnętrznego separatora pól ( $IFS), aby zdecydować, gdzie ten ciąg powinien zostać podzielony. Domyślnie dzieli się na linie, tabulatory i spacje, więc jeśli masz plik o nazwie new style.css, polecenia gzip newi gzip style.csszostaną wykonane.
Dennis
1
@Sadik, Dennis ma rację, ponieważ szybkie obejście można uruchomić export IFS=$'\n'tuż przed forpętlą.
mndo
14

użyłbym

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

Zmień namena, inamejeśli chcesz dopasować rozszerzenia bez rozróżniania wielkości liter (tj. Dołącz .CSSi / lub .HTMLrozszerzenia). Możesz pominąć opcję, /path/to/dirjeśli chcesz rozpocząć wyszukiwanie rekurencyjne z bieżącego katalogu.

steeldriver
źródło
2
Dla tych, którzy mogą się zastanawiać nad --keepprzełącznikiem, tak, powoduje to zachowanie oryginalnych plików. Pomiń, jeśli chcesz, aby zostały usunięte po zgzipowaniu.
Ben Johnson,
4

Aby uzyskać listę plików:

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

I gzip wszystkie te pliki:

find -type f | grep -P '\.js|\.html|\.css' | tar cvzf archive.gz -T -
chaos
źródło
Czy nie to lista plików jako wyjście przez , zamiast samych plików? tarfind
Jos
Zredagowałem swoje pytanie, aby wyjaśnić, że chcę mieć plik archiwum dla każdego pliku css, html lub js.
Sadik
2
@Jos nie z -Topcją tarprzetwarza dane wejściowe jako nazwy plików.
chaos
@chaos Ach, dziękuję. Nauczyłem się dziś czegoś.
Jos
2

Kiedyś odpowiedź steeldriver jest , ale chciałbym, aby zakończyć go z --besti --forceopcji.

cdw dowolnym folderze i wpisz ten kod. Wszystkie pasujące pliki zostaną skompresowane.

find . \( -name '*.css' -o -name '*.js' \) -exec gzip --verbose --keep --best --force {} \;
  • Użyj --bestdla najlepszego współczynnika kompresji.
  • Służy --forcedo zastępowania bez pytania, czy istnieje już plik spakowany gzip.
azerafati
źródło
1

Możesz użyć globstar.

Po włączeniu globstaropcji powłoki wszystko czego potrzebujesz to gzip -vk **/*.{css,html}.

Powłoka Bash ma globstaropcję, która pozwala pisać rekurencyjnych globs z **. shopt -s globstarumożliwia to. Ale możesz nie chcieć tego robić w przypadku innych poleceń uruchamianych później, aby zamiast tego można było uruchomić je i gzip polecenie w podpowłoce .

To polecenie gzips wszystkich .cssi .htmlplików w bieżącym katalogu dowolnego z jego podkatalogów, dowolnego z ich podkatalogów itp., Zachowując oryginalne pliki ( -k) i informując o tym, co robi ( -v):

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

Jeśli chcesz dopasować nazwy plików bez rozróżniania wielkości liter, aby te rozszerzenia z niektórymi lub wszystkimi literami były włączone, możesz również włączyć nocaseglobopcję powłoki:

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

;rozdziela dwa polecenia, a zewnętrzny ( )powoduje, że są one uruchamiane w podpowłoce. Ustawienie opcji powłoki w podpowłoce nie powoduje ustawienia jej w powłoce wywołującej. Jeśli nie chcesz, aby umożliwić globstarnastępnie można uruchomić shopt -s globstar; możesz po prostu uruchomić polecenie:

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

Możesz wyłączyć za globstarpomocą shopt -u globstar. Możesz sprawdzić, czy jest on obecnie włączony za pomocą shopt globstar.

Jak to działa

Kluczem do działania tego gzippolecenia jest to, że powłoka wykonuje na nim rozszerzenia, aby utworzyć listę każdego pliku w hierarchii katalogów o pasującej nazwie, a następnie przekazuje każdą z tych nazw plików jako argumenty gzip.

  • Ekspansja nawiasów zamienia się **/*.{css,html}w **/*.css **/*.html.
  • Następnie globbing rozszerza te dwa wzorce na nazwy plików dostępnych w bieżącym katalogu (z **powodu globstar), których nazwy plików składają się ze wszystkiego ( *), po którym następuje określony sufiks ( .csslub .htmlw tym przypadku).

To nie pasuje do plików, których nazwy zaczynają się od,. ani tych, które znajdują się w katalogach o takich nazwach. Prawdopodobnie nie masz takich plików HTML i CSS, a jeśli tak, prawdopodobnie nie chcesz ich dołączać. Ale jeśli chcesz je dołączyć, możesz je jednoznacznie dopasować w zależności od potrzeb. Na przykład zmiana **/*.{css,html}na **/{,.}*.{css,html}obejmuje pliki, które zaczynają się od, .ale nadal nie wyszukują w folderach, które to robią.

Jeśli chcesz uwzględnić zarówno pliki, których nazwy zaczynają się od, jak .i pliki w katalogach, których nazwy zaczynają się .od, to jest prostszy i prostszy sposób: włącz dotglobopcję powłoki.

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

Lub jeśli chcesz rozróżniać małe i wielkie litery oraz nazwy plików rozpoczynające się od .:

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

Możliwe jest, choć bardzo rzadkie, **rozwinięcie się w coś zbyt długiego.

Jeśli masz ogromną liczbę plików nazwanych w ten sposób, to może zakończyć się niepowodzeniem z komunikatem o błędzie, wyjaśniając, że powłoka nie można zbudować z wiersza poleceń, ponieważ byłoby to zbyt długo. (Nawet w przypadku tysięcy plików zwykle nie stanowi to problemu).

gzip nie zostanie w ogóle wezwany, więc nie dostaniesz w połowie wykonanej pracy.

Jeśli wystąpi ten błąd lub martwisz się nim, możesz użyć findgo -exec, tak jak opisuje steeldriver (z {} \;) lub jak opisuję poniżej (z {} +).

Możesz używać findz -execakcją i +dla wydajności.

Te gzippodpory polecenia są podane nazwy wielu plików mają być kompresowane. Ale to findpolecenie, chociaż działa dobrze i nie będzie wolne, chyba że masz wiele plików, uruchamia gzippolecenie raz dla każdego pliku:

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

To działa i na pewno możesz go użyć. ( .wyszukuje z bieżącego katalogu. Poza tym jest to naprawdę nieco inny sposób pisania polecenia w bardzo dobrej odpowiedzi steeldriver ; możesz użyć dowolnego stylu).

Możesz także findprzekazać wiele nazw plików gzipi uruchomić go tyle razy, ile to konieczne - co prawie zawsze jest tylko raz. Aby to zrobić, użyj +zamiast\; . +Argument powinien przyjść tuż po {}. findzastępuje +dodatkowymi nazwami plików, jeśli istnieją.

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

Można z niego korzystać, +nawet jeśli jest tylko kilka pasujących plików, a gdy jest ich wiele, może być zauważalnie szybsze niż oddzielne gzipwywołanie dla każdego pliku.

Jak steeldriver wspomina , można użyć -inamezamiast -namedopasować pliki, których nazwa koniec jak .cssalbo .htmlale o różnej kapitalizacji. Odpowiada to włączeniu nocaseglobw globstarmetodzie opisanej powyżej.

Wreszcie prawdopodobnie nie masz żadnych pasujących plików lub katalogów, które zaczynają się od .. Ale jeśli to zrobisz, findautomatycznie je uwzględni. Jeśli chcesz je wykluczyć (jak to się dzieje w przypadku globstarmetody opartej na szczegółach powyżej, gdy dotglobjest wyłączona), możesz :

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

globstar-Na sposób opisany powyżej jest prostsze pisać, zwłaszcza jeśli jesteś z wyłączeniem katalogów i plików, które zaczynają się ., ponieważ jest to ustawienie domyślne.

Czego nie robić ...

Nazwy plików mogą zawierać dowolny znak oprócz separatora ścieżki /i znaku zerowego . Istnieje wiele technik, które łamią się na dziwnych nazwach plików i są zwykle bardziej skomplikowane niż techniki, które zawsze działają. Sugeruję więc unikanie ich, nawet jeśli wiesz (lub wydaje ci się, że wiesz), że są w porządku w twojej konkretnej sytuacji. I oczywiście, że należy nie używać ich, czy może masz nazwy plików ze znakami, które mogą być traktowane specjalnie, łącznie ze spacjami.

Możliwe jest bezpieczne przesyłanie danych wyjściowych finddo innego polecenia, które je przetwarza, jeśli użyjesz -print0lub podobnej akcji, aby spowodować umieszczenie znaku pustego między ścieżkami zamiast nowej linii , a nie inaczej. Nazwy plików mogą zawierać znaki nowej linii (choć zniechęcam cię do celowego nazywania ich plikami). findKomenda z -printakcji - w tym polecenia find bez wyraźnego działania, ponieważ wtedy -printjest domyślna - nie powoduje wyjście, które można bezpiecznie doprowadzanej rurami lub inaczej dostarczane do innego polecenia, które wykonuje czynność na plikach.

Dane wyjściowe findtworzone za pomocą -print0akcji mogą być bezpiecznie przesyłane do potoku xargs -0( -0flaga informuje, xargsże należy oczekiwać danych wejściowych oddzielonych od wartości zerowej).

Eliah Kagan
źródło
0

Aby rekurencyjnie skompresować wszystkie pliki w folderze / podfolderze:

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

Odsunąć zamek błyskawiczny:

gunzip -r `find . -type f -name "*.gz"` 
Naruto_Hokage
źródło
Ta metoda podstawiania poleceń często psuje się i dość źle. Problem polega na tym, że nazwy plików zawierające spacje lub inne białe znaki są dzielone i traktowane jak wiele nazw plików. (Te polecenia są pisane przy użyciu ` `składni, ale problem w pełni dotyczy również korzystania ze $( )składni.)
Eliah Kagan