gzip todos los archivos con extensiones específicas

11

Estoy tratando de comprimir todos los archivos en ubuntu que tienen la extensión de archivo .css, .html o .js. en un directorio superior y todos los subdirectorios. Quiero conservar los archivos originales y sobrescribir el archivo .gz, si ya existe.

Entonces, cuando tengo n archivos, quiero mantener estos n archivos y crear n archivos de archivo adicionales. No sólo uno.

Mi intento fue ejecutar un script que se vea así:

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

Primero: necesito tener una línea en esa secuencia de comandos para cada extensión de archivo que quiero gzip. Eso está bien, pero espero encontrar una mejor manera

Segundo y más importante: no funciona. Aunque -r debería hacer el trabajo, los subdirectorios no cambian. El archivo gzip solo se crea en el directorio superior.

¿Que me estoy perdiendo aqui?

Por cierto: El siguiente es un error en la salida detallada, ¿verdad? Cuando se usa la opción -k y -v

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

El resultado detallado dice que reemplaza el archivo, aunque "reemplazar" significa que el archivo original no existe después del reemplazo. De todos modos, esto es solo lo de salida.

$ 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
fuente
1
-rFunciona según lo diseñado. Desde man gzip : recorre la estructura de directorios de forma recursiva. Si alguno de los nombres de archivo especificados en la línea de comando son directorios , gzip descenderá al directorio y comprimirá todos los archivos que encuentre allí (o los descomprimirá en el caso de gunzip). (énfasis mío)
Dennis
Okay. Entonces -r ingresaría a un directorio con el nombre XYZ.css. Entonces la recursión no está diseñada como esperaba.
Sadik

Respuestas:

7

puedes hacerlo con un bucle for para encontrar cada archivo y luego comprimirlo:

for i in `find | grep -E "\.css$|\.html$"`; do gzip "$i" ; done
mndo
fuente
¡Gracias! Aunque la -ropción no funciona, -ky -festá funcionando, puedo usarlos así: para i in find | grep -E "\.css$|\.html$"; do gzip -vkf "$ i"; hecho`
Sadik
@Sadik: ¡Ten cuidado! Este enfoque no funcionará si alguno de los nombres de los archivos contiene un espacio.
Dennis
¿Podría explicar por qué no?
Sadik
1
@Sadik: `...`proporciona una cadena, no una lista. forusa el separador de campo interno ( $IFS) para decidir dónde se debe dividir esa cadena. Por defecto, se divide en saltos de línea, pestañas y espacios, por lo que si tiene un archivo llamado new style.css, los comandos gzip newy gzip style.cssse ejecutarán.
Dennis
1
@Sadik, Dennis tiene razón, como solución rápida que puede ejecutar export IFS=$'\n'justo antes del forciclo.
mndo
14

yo usaría

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

Cambie namea inamesi desea hacer coincidir las extensiones entre mayúsculas y minúsculas (es decir, incluir .CSSy / o .HTMLextensiones). Puede omitir el /path/to/dirsi desea iniciar la búsqueda recursiva desde el directorio actual.

conductor de acero
fuente
2
Para aquellos que se estén preguntando sobre el --keepcambio, sí, hace que se retengan los archivos originales. Omítelo si desea que se eliminen una vez comprimidos.
Ben Johnson
4

Para obtener la lista de archivos:

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

Y para comprimir todos esos archivos:

find -type f | grep -P '\.js|\.html|\.css' | tar cvzf archive.gz -T -
caos
fuente
¿No sería esta tarla lista de archivos como salida find, en lugar de los archivos en sí mismos?
Jos
Edité mi pregunta para dejar en claro que quiero tener un archivo para cada archivo css, html o js.
Sadik
2
@Jos no con la -Topción tarprocesa la entrada como nombres de archivo.
caos
@chaos Ah, gracias. Aprendí algo hoy.
Jos
2

Solía respuesta de steeldriver , pero me gusta para terminar con el --besty --forceopciones.

cden cualquier carpeta y escriba este código. Todos sus archivos coincidentes serán comprimidos.

find . \( -name '*.css' -o -name '*.js' \) -exec gzip --verbose --keep --best --force {} \;
  • Úselo --bestpara obtener la mejor relación de compresión.
  • Úselo --forcepara sobrescribir sin preguntar si ya hay un archivo comprimido.
azerafati
fuente
1

Puedes usar globstar.

Con la globstaropción de shell habilitada, todo lo que necesita es gzip -vk **/*.{css,html}.

El shell Bash tiene una globstaropción que te permite escribir globos recursivos con **. shopt -s globstarlo habilita Pero es posible que no desee hacer eso para otros comandos que ejecute más tarde, por lo que puede ejecutarlo junto con su gzip comando en una subshell .

Este comando gzipes todo .cssy .htmllos archivos en el directorio actual de cualquiera de sus subdirectorios, cualquiera de sus subdirectorios, etc., manteniendo los archivos originales ( -k) y que lo que está haciendo (diciendo -v):

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

Si desea hacer coincidir los nombres de archivo sin distinción entre mayúsculas y minúsculas para que se incluyan esas extensiones con algunas o todas las letras en mayúscula, también puede habilitar la nocaseglobopción de shell:

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

;separa los dos comandos, y el exterior ( )hace que se ejecuten en una subshell. Establecer una opción de shell en un subshell no hace que se establezca en el shell de llamada. Si no desea habilitar globstarcontinuación, puede ejecutar shopt -s globstar; entonces puedes ejecutar el comando:

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

Puede deshabilitar globstarcon shopt -u globstar. Puede verificar si está habilitado actualmente con shopt globstar.

Cómo funciona

La clave de cómo funciona este gzipcomando es que el shell realiza expansiones en él para producir una lista de cada archivo en la jerarquía del directorio con un nombre coincidente, luego pasa cada uno de estos nombres de archivo como argumentos gzip.

  • La expansión de llaves se convierte **/*.{css,html}en **/*.css **/*.html.
  • Luego, globbing expande esos dos patrones en los nombres de archivos accesibles bajo el directorio actual ( **debido a globstar) cuyos nombres de archivo consisten en cualquier cosa ( *) seguido del sufijo especificado ( .csso .htmlen este caso).

Esto no coincide con los archivos cuyos nombres comienzan con. los que residen en directorios nombrados de esta manera. Probablemente no tenga dichos archivos HTML y CSS y, si los tiene, probablemente no quiera incluirlos. Pero si desea incluirlos, puede hacerlos coincidir explícitamente según sus necesidades. Por ejemplo, cambiar **/*.{css,html}a **/{,.}*.{css,html}incluye archivos que comienzan .mientras aún no buscan en carpetas que sí lo hacen.

Si desea que se incluyan tanto los archivos cuyos nombres comiencen .como los archivos en directorios cuyos nombres comiencen con ., hay una forma más limpia y sencilla: habilitar la dotglobopción de shell.

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

O si desea la coincidencia entre mayúsculas y minúsculas y la coincidencia de nombres de archivo que comienzan con .:

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

Es posible, aunque muy raro, **expandirse a algo demasiado tiempo.

Si tiene una gran cantidad de archivos nombrados de esta manera, esto puede fallar con un mensaje de error que explica que el shell no puede construir la línea de comando porque sería demasiado largo. (Incluso con miles de archivos, esto generalmente no es un problema).

gzip no será llamado en absoluto, por lo que no obtendrá un trabajo a medio hacer.

Si ocurre este error, o si le preocupa, puede usarlo findcon -exec, como lo describe steeldriver (con {} \;) o como lo describo a continuación (con {} +).

Se puede usar findcon la -execacción y +para la eficiencia.

El gzipcomando admite recibir nombres de varios archivos para comprimir. Pero este findcomando, aunque funciona bien y no será lento a menos que tenga muchos archivos, ejecuta el gzipcomando una vez para cada archivo:

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

Esto funciona, y definitivamente puedes usarlo. ( .busca desde el directorio actual. Además de eso, es realmente una forma ligeramente diferente de escribir el comando en la muy buena respuesta de steeldriver ; puede usar el estilo que prefiera).

También puede findpasar varios nombres de archivo gzipy ejecutarlo solo las veces que sea necesario, que casi siempre es solo una vez. Para hacer eso, use en +lugar de\; . El +argumento debería venir justo después {}. findreemplaza +con nombres de archivo adicionales, si los hay.

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

Está bien usarlo +incluso si solo hay unos pocos archivos coincidentes, y cuando hay muchos de ellos, puede ser notablemente más rápido que tener una gzipinvocación separada para cada archivo.

Como menciona Steeldriver , puede usar en -inamelugar de -namehacer coincidir archivos cuyo nombre termine como .csso .htmlpero con diferentes mayúsculas. Esto corresponde a habilitar nocasegloben el globstarmétodo basado en lo descrito anteriormente.

Finalmente, probablemente no tenga ningún archivo o directorio coincidente que comience con .. Pero si lo hace, findlos incluye automáticamente. Si desea excluirlos (como sucede con el globstarmétodo basado en detalles detallado anteriormente cuando dotglobestá desactivado), puede :

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

La globstarforma basada en lo descrito anteriormente es más simple de escribir, especialmente si excluye directorios y archivos que comienzan ., ya que ese es el valor predeterminado.

Que no hacer ...

Los nombres de archivo pueden contener cualquier carácter, excepto el separador de ruta /y el carácter nulo . Existen muchas técnicas que se rompen en nombres de archivo extraños, y generalmente son más complicadas que las técnicas que siempre funcionan. Por lo tanto, sugiero evitarlos incluso cuando sepa (o piense que sabe) que están bien en su situación específica. Y, por supuesto, no debe usarlos si puede tener nombres de archivo con caracteres que pueden tratarse especialmente, incluidos los espacios.

Es posible canalizar de forma segura la salida de findotro comando que lo procesa si usa -print0o una acción similar para hacer que coloque un carácter nulo entre las rutas en lugar de una nueva línea , y no de otra manera. Los nombres de archivo pueden contener nuevas líneas (aunque te desaliento de nombrar deliberadamente archivos con ellos). Un findcomando con la -printacción, incluidos los comandos de búsqueda sin acción explícita, ya que -printes el valor predeterminado, no produce resultados que se puedan canalizar de forma segura o de otro modo a otro comando que realice una acción en los archivos.

La salida que se findproduce con la -print0acción se puede canalizar de forma segura xargs -0(el -0indicador indica que xargsdebe esperar una entrada separada por nulos).

Eliah Kagan
fuente
0

Para comprimir todos los archivos en una carpeta / subcarpeta de forma recursiva:

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

Descomprimir:

gunzip -r `find . -type f -name "*.gz"` 
Naruto_Hokage
fuente
Este método basado en la sustitución de comandos se romperá con frecuencia, y bastante mal. El problema es que los nombres de archivo que contienen espacios u otros espacios en blanco se dividirán y tratarán como múltiples nombres de archivo. (Estos comandos se escriben usando la ` `sintaxis, pero el problema se aplica completamente cuando se usa también la $( )sintaxis.)
Eliah Kagan