gzip tutti i file con estensioni specifiche


11

Sto cercando di decomprimere tutti i file su Ubuntu con estensione .css, .html o .js. in una directory superiore e in tutte le sottodirectory. Voglio mantenere i file originali e sovrascrivere il file .gz, se già esistente.

Quindi quando ho n file, voglio conservare questi n file e creare altri n file di archivio. Non solo uno.

Il mio tentativo è stato quello di eseguire uno script simile al seguente:

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

Primo: ho bisogno di avere una riga in quello script per ogni estensione di file che voglio gzip. Va bene, ma spero di trovare un modo migliore

Secondo e più importante: non funziona. Sebbene -r dovrebbe fare il lavoro, le sottodirectory sono invariate. Il file gzip viene creato solo nella directory principale.

Cosa mi sto perdendo qui?

Btw: Di seguito è riportato un bug nell'output dettagliato, giusto? Quando si usano le opzioni -k e -v

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

L'output dettagliato dice che sostituisce il file, anche se "sostituisci" significa che il file originale non esiste dopo la sostituzione. Comunque, questa è solo la cosa in uscita.

$ 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

1
-rfunziona come progettato. Da man gzip : viaggiare ricorsivamente nella struttura delle directory. Se uno dei nomi di file specificati sulla riga di comando è una directory , gzip scenderà nella directory e comprimerà tutti i file che trova lì (o li decomprimerà nel caso di gunzip). (enfasi mia)
Dennis,

Ok. Quindi -r inserisce una directory con il nome XYZ.css. Quindi la ricorsione non è progettata come mi aspettavo.
Sadik,

Risposte:


7

puoi farlo con un ciclo for per trovare ogni file quindi comprimerlo:

for i in `find | grep -E "\.css$|\.html$"`; do gzip "$i" ; done

Grazie! Sebbene l' -ropzione non funzioni -ke funzioni, -fquindi posso usarli in questo modo: for in find | grep -E "\.css$|\.html$"; esegui gzip -vkf "$ i"; done`
Sadik,

@Sadik: stai attento! Questo approccio non funzionerà se uno dei nomi dei file contiene uno spazio.
Dennis,

Potresti spiegare perché no?
Sadik,

1
@Sadik: `...`fornisce una stringa, non un elenco. forutilizza il separatore di campo interno ( $IFS) per decidere dove suddividere quella stringa. Per impostazione predefinita, si divide in avanzamenti di riga, schede e spazi, quindi se si dispone di un file chiamato new style.css, i comandi gzip newe gzip style.cssverranno eseguiti.
Dennis,

1
@Sadik, Dennis ha ragione, come soluzione rapida puoi correre export IFS=$'\n'poco prima del forloop.
mndo,

14

io userei

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

Passare namea inamese si desidera abbinare le estensioni senza distinzione tra maiuscole e minuscole (ovvero includere .CSSe / o .HTMLestensioni). È possibile omettere /path/to/dirse si desidera avviare la ricerca ricorsiva dalla directory corrente.


2
Per coloro che potrebbero chiedersi il --keeppassaggio, sì, mantiene i file originali. Omettilo se vuoi che vengano eliminati una volta compressi.
Ben Johnson,

4

Per ottenere l'elenco dei file:

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

E per decomprimere tutti quei file:

find -type f | grep -P '\.js|\.html|\.css' | tar cvzf archive.gz -T -

Non sarebbe questo tarl' elenco dei file come output find, piuttosto che i file stessi?
Jos

Ho modificato la mia domanda per chiarire che voglio avere un file di archivio per ogni file css, html o js.
Sadik,

2
@Jos no con l' -Topzione tarelabora l'input come nomi di file.
caos,

@chaos Ah, grazie. Ho imparato qualcosa oggi.
Jos

2

Ho usato la risposta di Steeldriver , ma mi piace completarla con le opzioni --beste --force.

cdin qualsiasi cartella e digitare questo codice. Tutti i file corrispondenti verranno compressi con gzip.

find . \( -name '*.css' -o -name '*.js' \) -exec gzip --verbose --keep --best --force {} \;
  • Utilizzare --bestper il miglior rapporto di compressione.
  • Utilizzare --forceper la sovrascrittura senza chiedere se esiste già un file gzip.

1

Puoi usare globstar.

Con l' globstaropzione shell abilitata, tutto ciò che serve è gzip -vk **/*.{css,html}.

La shell Bash ha globstarun'opzione che ti consente di scrivere globi ricorsivi con **. shopt -s globstarlo abilita. Ma potresti non voler farlo per altri comandi che esegui in seguito, quindi puoi eseguirlo e il tuo gzip comando in una subshell .

Questo comando gzipè tutto .csse .htmlfile nella directory corrente una delle sue sottodirectory, una qualsiasi delle loro sottodirectory, ecc., Mantenendo i file originali ( -k) e raccontandoti cosa sta facendo ( -v):

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

Se si desidera abbinare i nomi dei file senza distinzione tra maiuscole e minuscole in modo da includere quelle estensioni con alcune o tutte le lettere maiuscole, è possibile anche abilitare l' nocaseglobopzione shell:

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

;separa i due comandi e l'esterno fa ( )sì che vengano eseguiti in una subshell. L'impostazione di un'opzione shell in una subshell non comporta l'impostazione nella shell chiamante. Se non desidera abilitare globstarallora si può eseguire shopt -s globstar; quindi puoi semplicemente eseguire il comando:

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

Puoi disabilitare globstarcon shopt -u globstar. Puoi verificare se è attualmente abilitato con shopt globstar.

Come funziona

La chiave per il funzionamento di questo gzipcomando è che la shell esegue espansioni su di esso per produrre un elenco di ciascun file nella gerarchia di directory con un nome corrispondente, quindi passa ciascuno di questi nomi di file come argomenti a gzip.

  • L'espansione del controvento si trasforma **/*.{css,html}in **/*.css **/*.html.
  • Quindi il globbing espande questi due modelli nei nomi dei file accessibili nella directory corrente ( **, a causa di globstar) i cui nomi di file sono costituiti da qualcosa ( *) seguito dal suffisso specificato ( .csso .htmlin questo caso).

Questo non corrisponde ai file i cui nomi iniziano con. o quelli che risiedono nelle directory denominate in questo modo. Probabilmente non hai tali file HTML e CSS e, se lo fai, probabilmente non vuoi includerli. Ma se vuoi includerli, puoi abbinarli esplicitamente a seconda delle tue esigenze. Ad esempio, il passaggio **/*.{css,html}a **/{,.}*.{css,html}include i file che iniziano con .mentre non si cerca ancora nelle cartelle che lo fanno.

Se vuoi che siano inclusi sia i file i cui nomi iniziano .che i file nelle directory i cui nomi iniziano con ., c'è un modo più semplice e pulito: abilita l' dotglobopzione shell.

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

O se si desidera la corrispondenza senza distinzione tra maiuscole e minuscole e la corrispondenza dei nomi di file che iniziano con .:

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

È possibile, sebbene molto raro, **espandersi a qualcosa di troppo lungo.

Se si dispone di un numero enorme di file denominati in questo modo, ciò potrebbe non riuscire con un messaggio di errore che spiega che la shell non può costruire la riga di comando perché sarebbe troppo lunga. (Anche con migliaia di file, questo di solito non è un problema.)

gzip non verrà chiamato affatto, quindi non otterrai un lavoro a metà.

Se si verifica questo errore, o se sei preoccupato, puoi usarlo findcon -exec, come descrive steeldriver (con {} \;) o come descrivo di seguito (con {} +).

È possibile utilizzare findcon l' -execazione e +per l'efficienza.

Il gzipcomando supporta l'assegnazione di nomi di più file da comprimere. Ma questo findcomando, sebbene funzioni bene e non sarà lento a meno che tu non abbia molti file, esegue il gzipcomando una volta per ogni file:

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

Funziona e puoi sicuramente usarlo. ( .cerca dalla directory corrente. Oltre a questo, è davvero un modo leggermente diverso di scrivere il comando nella risposta molto buona di steeldriver ; puoi usare qualunque stile tu preferisca.)

Puoi anche far findpassare più nomi di file gziped eseguirlo solo tutte le volte necessarie, il che è quasi sempre solo una volta. Per farlo, usa +invece di\; . L' +argomento dovrebbe venire subito dopo {}. findsostituisce +con eventuali nomi di file aggiuntivi.

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

Va bene usare +anche se ci sono solo pochi file corrispondenti e, quando ce ne sono molti, può essere notevolmente più veloce di avere una gzipchiamata separata per ogni file.

Come menziona steeldriver , è possibile utilizzare -inameinvece di -nameabbinare i file il cui nome termina come .csso .htmlma con lettere maiuscole diverse. Ciò corrisponde all'abilitazione nocaseglobnel globstarmetodo basato su sopra descritto.

Infine, probabilmente non hai file o directory corrispondenti che iniziano con .. Ma se lo fai, findli include automaticamente. Se si desidera escluderli (come accade con il globstarmetodo basato su dettagliato sopra quando dotglobè disattivato), è possibile :

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

Il globstarmodo basato sopra descritto è più semplice da scrivere, soprattutto se si escludono directory e file che iniziano con ., poiché questo è il valore predefinito.

Cosa non fare ...

I nomi di file possono contenere qualsiasi carattere tranne il separatore di percorso /e il carattere null . Esistono molte tecniche che si rompono su nomi di file strani e di solito sono più complicate delle tecniche che funzionano sempre. Quindi suggerisco di evitarli anche quando sai (o pensi di sapere) che stanno bene nella tua situazione specifica. E ovviamente non devi usarli se potresti avere nomi di file con caratteri che possono essere trattati in modo speciale, compresi gli spazi.

È possibile reindirizzare in modo sicuro l'output di findun altro comando che lo elabora se si utilizza -print0o un'azione simile per indurlo a posizionare un carattere null tra i percorsi anziché una nuova riga e non altrimenti. I nomi dei file possono contenere newline (anche se ti scoraggio dal nominare deliberatamente i file con loro). Un findcomando con l' -printazione - incluso il comando find senza azione esplicita, poiché da allora -printè l'impostazione predefinita - non produce output che può essere convogliato in sicurezza o altrimenti fornito a un altro comando che esegue un'azione sui file.

L'output findprodotto con l' -print0azione può essere tranquillamente reindirizzato xargs -0(il -0flag indica xargsdi prevedere un input separato da null).


0

Per comprimere tutti i file in una cartella / sottocartella in modo ricorsivo:

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

Per decomprimere:

gunzip -r `find . -type f -name "*.gz"` 

Questo metodo basato sulla sostituzione dei comandi si interromperà spesso e abbastanza male. Il problema è che i nomi di file contenenti spazi o altri spazi bianchi verranno suddivisi e trattati come nomi di file multipli. (Questi comandi sono scritti usando la ` `sintassi, ma il problema si applica anche quando si usa la $( )sintassi.)
Eliah Kagan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.