gzip todos os arquivos com extensões específicas

11

Estou tentando compactar todos os arquivos no ubuntu com extensão de arquivo .css, .html ou .js. em um diretório superior e em todos os subdiretórios. Desejo manter os arquivos originais e substituir o arquivo .gz, se já existir.

Portanto, quando tenho n arquivos, quero mantê-los e criar n arquivos adicionais. Não é só um.

Minha tentativa foi executar um script que se parece com isso:

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

Primeiro: preciso ter uma linha nesse script para cada extensão de arquivo que eu quero compactar. Tudo bem, mas espero encontrar uma maneira melhor

Segundo e mais importante: não funciona. Embora -r deva fazer o trabalho, os subdiretórios não são alterados. O arquivo gzip é criado apenas no diretório superior.

O que estou perdendo aqui?

Btw: A seguir, há um erro na saída detalhada, certo? Ao usar a opção -k e -v

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

A saída detalhada diz que substitui o arquivo, embora "substituir" signifique que o arquivo original não existe após a substituição. Enfim, isso é apenas a saída.

$ 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
fonte
1
-rfunciona como projetado. No man gzip : Percorra a estrutura de diretórios recursivamente. Se qualquer um dos nomes de arquivos especificados na linha de comando forem diretórios , o gzip descerá para o diretório e compactará todos os arquivos que encontrar lá (ou descompactá-los no caso do gunzip). (ênfase meu)
Dennis
Está bem. Então -r entraria em um diretório com o nome XYZ.css. Então a recursão não é projetada como eu esperava.
Sadik

Respostas:

7

você pode fazer isso com um loop for para encontrar todos os arquivos e compactá-los:

for i in `find | grep -E "\.css$|\.html$"`; do gzip "$i" ; done
desfazer
fonte
Obrigado! Embora a -ropção não funcione -ke -festeja funcionando, eu posso usá-las assim: para i in find | grep -E "\.css$|\.html$"; faça gzip -vkf "$ i"; done` #
Sadik
@ Sadik: Tenha cuidado! Essa abordagem não funcionará se algum dos nomes dos arquivos contiver um espaço.
1111 Dennis
Você poderia explicar por que não?
Sadik
1
@ Sadik: `...`fornece uma string, não uma lista. forusa o separador de campo interno ( $IFS) para decidir onde essa sequência deve ser dividida. Por padrão, ela se divide em Linefeeds, tabulações e espaços, por isso, se você tem um arquivo chamado new style.css, os comandos gzip newe gzip style.cssserá executado.
Dennis
1
@ Sadik, Dennis está certo, como solução rápida você pode executar export IFS=$'\n'imediatamente antes do forloop.
mndo 8/08/14
14

eu usaria

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

Altere namepara inamese você deseja corresponder as extensões sem distinção entre maiúsculas e minúsculas (por exemplo, incluir .CSSe / ou .HTMLextensões). Você pode omitir o /path/to/dirse desejar iniciar a pesquisa recursiva no diretório atual.

chave de aço
fonte
2
Para aqueles que podem estar se perguntando sobre a --keepopção, sim, isso faz com que os arquivos originais sejam mantidos. Omita-o se quiser que eles sejam excluídos depois de compactados com gzip.
Ben Johnson
4

Para obter a lista de arquivos:

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

E para compactar todos esses arquivos:

find -type f | grep -P '\.js|\.html|\.css' | tar cvzf archive.gz -T -
caos
fonte
Não seria essa tara lista de arquivos como saída find, em vez dos próprios arquivos?
11554 Jos Jos
Editei minha pergunta para deixar claro que quero ter um arquivo morto para cada arquivo css, html ou js.
Sadik
2
@Jos no com a -Topção tarprocessa a entrada como nomes de arquivos.
caos
@chaos Ah, obrigado. Eu aprendi algo hoje.
11554 Jos Jos
2

Eu usei a resposta de steeldriver , mas eu gosto de completá-lo com o --beste --forceopções.

cdem qualquer pasta e digite este código. Todos os seus arquivos correspondentes serão compactados em gzip.

find . \( -name '*.css' -o -name '*.js' \) -exec gzip --verbose --keep --best --force {} \;
  • Use --bestpara a melhor taxa de compactação.
  • Use --forcepara substituir sem perguntar se já existe um arquivo compactado em gzip.
azerafati
fonte
1

Você pode usar globstar.

Com a globstaropção de shell ativada, tudo que você precisa é gzip -vk **/*.{css,html}.

O shell Bash tem uma globstaropção que permite que você escreva recursiva globs com **. shopt -s globstarpermite. Mas você pode não querer fazer isso para outros comandos executados posteriormente, para que você possa executá-lo e seu gzip comando em uma subshell .

Este comando gzipé tudo .csse .htmlarquivos no diretório atual qualquer de seus subdiretórios, qualquer um de seus subdiretórios, etc., mantendo os arquivos originais ( -k) e dizendo-lhe o que está fazendo ( -v):

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

Se você deseja corresponder os nomes dos arquivos sem distinção entre maiúsculas e minúsculas, para que aquelas extensões com algumas ou todas as letras maiúsculas sejam incluídas, também é possível ativar a nocaseglobopção shell:

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

;separa os dois comandos e o externo ( )faz com que eles sejam executados em um subshell. Definir uma opção de shell em um subshell não faz com que ela seja definida no shell de chamada. Se você não deseja ativar globstar, em seguida, você pode executar shopt -s globstar; então você pode simplesmente executar o comando:

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

Você pode desativar globstarcom shopt -u globstar. Você pode verificar se está ativado no momento shopt globstar.

Como funciona

A chave de como esse gzipcomando funciona é que o shell execute expansões nele para produzir uma lista de cada arquivo na hierarquia de diretórios com um nome correspondente e, em seguida, transmita cada um desses nomes de arquivo como argumentos para gzip.

  • A expansão da cinta se **/*.{css,html}transforma **/*.css **/*.html.
  • Em seguida, o globbing expande esses dois padrões para os nomes de arquivos acessíveis no diretório atual ( **devido a globstar) cujos nomes de arquivos consistem em qualquer coisa ( *) seguida pelo sufixo especificado ( .cssou .htmlneste caso).

Isso não corresponde aos arquivos cujos nomes começam com. ou aos que residem nos diretórios nomeados dessa maneira. Você provavelmente não possui esses arquivos HTML e CSS e, se tiver, provavelmente não deseja incluí-los. Mas se você quiser incluí-los, poderá correspondê-los explicitamente, dependendo de suas necessidades. Por exemplo, alterar **/*.{css,html}para **/{,.}*.{css,html}inclui arquivos que começam com .ainda não pesquisam nas pastas que o fazem.

Se você deseja que os arquivos cujos nomes começam com .e os diretórios cujos nomes começam .sejam incluídos, existe uma maneira mais limpa e simples: habilite a dotglobopção shell.

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

Ou se você deseja correspondência que não diferencia maiúsculas de minúsculas e correspondência de nomes de arquivos que começam com .:

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

É possível, embora muito raro, **expandir para algo muito longo.

Se você tiver um grande número de arquivos nomeados dessa maneira, isso poderá falhar com uma mensagem de erro explicando que o shell não pode criar a linha de comando porque seria muito longo. (Mesmo com milhares de arquivos, isso geralmente não é um problema.)

gzip não será chamado, então você não terá um trabalho pela metade.

Se esse erro ocorrer, ou se você estiver preocupado com isso, poderá usá-lo findcom -exec, como a steeldriver descreve (com {} \;) ou como eu descrevo abaixo (com {} +).

Você pode usar findcom a -execação e +obter eficiência.

O gzipcomando suporta receber nomes de vários arquivos a serem compactados. Mas este findcomando, embora funcione bem e não seja lento, a menos que você tenha muitos arquivos, executa o gzipcomando uma vez para cada arquivo:

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

Isso funciona e você pode definitivamente usá-lo. ( .pesquisa no diretório atual. Além disso, é realmente uma maneira ligeiramente diferente de escrever o comando na resposta muito boa do steeldriver ; você pode usar o estilo que preferir.)

Você também pode findpassar vários nomes de arquivos para gzipe executá-lo apenas quantas vezes forem necessárias - o que é quase sempre apenas uma vez. Para fazer isso, use em +vez de\; . O +argumento deve vir logo depois {}. findsubstitui +por nomes de arquivos adicionais, se houver.

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

É bom usá-lo +mesmo se houver apenas alguns arquivos correspondentes e, quando houver muitos deles, poderá ser visivelmente mais rápido do que ter uma gzipchamada separada para cada arquivo.

Como a steeldriver menciona , é possível usar, em -inamevez de, -namepara corresponder aos arquivos cujo nome termina como .cssou .htmlmas com letras maiúsculas diferentes. Isso corresponde à habilitação nocaseglobno globstarmétodo baseado em descrito acima.

Finalmente, você provavelmente não possui nenhum arquivo ou diretório correspondente que comece com .. Mas se você o fizer, findinclui-os automaticamente. Se você deseja excluí- los (como acontece com o globstarmétodo baseado em detalhes detalhado acima quando dotglobdesativado), você pode :

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

A globstarmaneira baseada em descrita acima é mais simples de escrever, especialmente se você estiver excluindo diretórios e arquivos que começam com ., pois esse é o padrão.

O que não fazer ...

Os nomes de arquivos podem conter qualquer caractere, exceto o separador de caminho /e o caractere nulo . Existem muitas técnicas que quebram em nomes de arquivos estranhos, e geralmente são mais complicadas do que técnicas que sempre funcionam. Então, sugiro evitá-los, mesmo quando você sabe (ou pensa que sabe) que eles estão bem em sua situação específica. E, é claro, você não deve usá-los se tiver nomes de arquivos com caracteres que possam ser tratados especialmente, incluindo espaços.

É possível canalizar com segurança a saída findpara outro comando que a processa se você usar -print0uma ação semelhante para fazer com que ele coloque um caractere nulo entre os caminhos, em vez de uma nova linha , e não o contrário. Os nomes de arquivos podem conter novas linhas (embora eu o desencoraje de nomear arquivos deliberadamente com eles). Um findcomando com a -printação - incluindo comandos find sem ação explícita, desde então -printé o padrão - não produz saída que possa ser canalizada com segurança ou fornecida de outro modo a outro comando que executa uma ação nos arquivos.

A saída findproduzida com a -print0ação pode ser canalizada com segurança xargs -0(o -0sinalizador indica xargspara esperar uma entrada separada por nulo).

Eliah Kagan
fonte
0

Para compactar todos os arquivos em uma pasta / subpasta recursivamente:

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

Para descompactar:

gunzip -r `find . -type f -name "*.gz"` 
Naruto_Hokage
fonte
Esse método baseado em substituição de comandos frequentemente quebra e é bastante ruim. O problema é que os nomes de arquivos que contêm espaços ou outros espaços em branco serão divididos e tratados como vários nomes de arquivos. (Estes comandos são escritos usando ` `a sintaxe, mas o problema se aplica plenamente ao usar a $( )sintaxe também.)
Elias Kagan