Como converter todos os arquivos do gzip para o xz na hora (e recursivamente)?

4

Eu tenho uma árvore de diretórios com arquivos compactados como este:

basedir/a/file.dat.gz
basedir/b/file.dat.gz
basedir/c/file.dat.gz
etc.

Como posso converter tudo isso de gzip para xz com um único comando e sem descompactar cada arquivo em disco?

O trivial two-liner com descompressão para o disco se parece com isso:

find basedir/ -type f -name '*.dat.gz' -exec gzip -d {} \;
find basedir/ -type f -name '*.dat' -exec xz {} \;

O primeiro comando pode ser menor: gunzip -r *

Para um único arquivo, a conversão dinâmica é simples (embora isso não substitua o arquivo .gz):

gzip -cd basedir/a/file.dat.gz | xz > basedir/a/file.dat.xz

Como o gzip e o xz estão lidando com as extensões em si, eu gostaria de dizer:

gunzip -rc * > xz

Eu olhei para find | xargs basename -s .gz { } , mas não consegui uma solução de trabalho.

Eu poderia escrever um script de shell, mas acho que deveria haver uma solução simples.

Editar

Obrigado por todos que já responderam. Eu sei que todos nós amamos 'comandos que nunca falharão'. Então, para manter isso simples:

  • Todos os subdiretórios contêm apenas números, letras (äöü, no entanto), sublinhado e menos.
  • Todos os arquivos são nomeados file.dat [.n] .gz, n sendo um inteiro positivo
  • Nenhum diretório ou arquivo terá um '.gz' em qualquer lugar (além do sufixo do arquivo final).
  • Esse é o único conteúdo que esses diretórios contêm.
  • Eu controlo a nomenclatura e posso restringi-la, se necessário.

Usando um simples find -exec ... ou ls | xargs , existe um comando para substituir '.gz' no nome encontrado por '.xz' on the fly? Então eu poderia escrever algo como (pseudo):

find basedir/ -type f -name '*.gz' -exec [ gzip -cd {} | xz > {replace .gz by .xz} \; ]
    
por Martin Hennings 08.09.2016 / 17:20

4 respostas

9

find . -name '*.gz' -type f -exec bash -o pipefail -Cc '
  for file do
    gunzip < "$file" | xz > "${file%.gz}.xz" && rm -f "$file"
  done' bash {} +

O -C evita sobrescrever um arquivo existente e não seguirá os links simbólicos exceto se o arquivo existente for um arquivo não regular ou um link para um arquivo não regular, portanto você não perder dados, a menos que você tenha, por exemplo, um file.gz e um file.xz , que é um link simbólico para /dev/null . Para proteger-se disso, você pode usar zsh e também usar o recurso -execdir de algumas find implementações para uma boa medida e evitar algumas condições de corrida:

find . -name '*.gz' -type f -execdir zsh -o pipefail -c '
  zmodload zsh/system || exit
  for file do
    gunzip < "$file" | (
      sysopen -u 1 -w -o excl -- "${file%.gz}.xz" && xz) &&
      rm -f -- "$file"
  done' zsh {} +

Ou para limpar xz arquivos em recompressões com falha:

find . -name '*.gz' -type f -execdir zsh -o pipefail -c '
  zmodload zsh/system || exit
  for file do
    sysopen -u 1 -w -o excl -- "${file%.gz}.xz" &&
      if gunzip < "$file" | xz; then
        rm -f -- "$file"
      else
        rm -f -- "${file%.gz}.xz"
      fi
  done' zsh {} +

Se você preferir que seja curto e esteja pronto para ignorar alguns desses possíveis problemas, em zsh , você pode fazer

for f (./**/*.gz(D.)) {gunzip < $f | xz > $f:r.xz && rm -f $f}
    
por 08.09.2016 / 17:25
2

Eu gosto de simples for loops ...

for file in basedir/*/*.gz
do
    gzip -cd < "$file" | xz > "${file%%.gz}.xz"
done

... pelo menos, se sua estrutura de diretórios for regular e simples o suficiente. Se você tiver que atravessar para profundidades desconhecidas ou condições adicionais na seleção de arquivos, você ainda terá que ficar com find ou similar.

    
por 08.09.2016 / 18:04
0
find basedir/ -type f -name '*.dat.gz'|while read -r line; do
 gzip -cd "$line" | xz > ${line%.gz}.xz
 rm "$line"
done
    
por 08.09.2016 / 17:24
0

Você pode fazer isso com encontrar e paralelizar

parallel -0 'gzip -cd '{}' | xz > '{.}'.xz; rm '{}'' < <(find basedir -iname \*gz -print0)

Etapas concluídas:

  • recursivo localiza todos os arquivos terminados em gz (sem distinção entre maiúsculas e minúsculas)
  • Stdin da substituição do processo
  • paralelo gzip foo.gz | xz > {foo} .xz; rm foo.gz
    • {.} remove o .gz de foo.gz (no meu entendimento)
por 08.09.2016 / 18:19