Como posso descompactar arquivos gz em diretórios aleatórios e armazenar os arquivos descompactados nesses mesmos diretórios?

3

CentOS 5.9

Eu tenho um servidor com um arquivo foo.ext.gz localizado em uma variedade de diretórios aleatórios.

Exemplo:

  • /opt/fooapp/foosubdirectory/foo_randomnumber/blah/blah/foo.ext.gz
  • /opt/fooapp/foosubdirectory/foo_differentrandomnumber/blah/blah/foo.ext.gz
  • /opt/fooapp/foosubdirectory/foo_anotherdifferentrandomnumber/blah/blah/foo.ext.gz

Gostaria de executar um comando bash que:

  1. Localizar arquivos foo.ext.gz
  2. Extraia o conteúdo do arquivo gz no mesmo diretório em que seu respectivo arquivo GZ reside
  3. Mantenha o arquivo gz original intacto.

Se eu estivesse fazendo isso manualmente, começaria com find / -iname foo.ext.gz . Depois disso, copiei o diretório em que o arquivo reside e digite algo como:

gunzip -c /opt/fooapp/foosubdirectory/foo_12345/blah/blah/foo.ext.gz  > /opt/fooapp/foosubdirectory/foo_12345/blah/blah/foo.ext

O problema aqui é que eu preciso fazer manualmente este processo para várias dezenas de arquivos / diretórios.

Existe uma maneira de eu alavancar xargs ou um loop for?

    
por Mike B 09.05.2013 / 08:45

4 respostas

5

Embora seja possível analisar a saída de um find , você precisa cuidar de espaços, etc. Infelizmente, gunzip não tem um sinal --keep / -k para manter (como bzip2 e xz tem).

Se um script pequeno gunzipkeep exigir um parâmetro (o arquivo gzipado) e fizer a descompactação, coloque esse script em algum lugar em seu $ PATH e chame-o com:

find /opt/fooapp/foosubdirectory -name "foo.ext.gz" -print0 | xargs -0 --norun-if-empty --max-args 1 gunzipkeep

O script pode ser algo como:

#!/bin/bash
inname=$1
outname=${inname%.gz}

gunzip -c "$inname" > "$outname"
    
por 09.05.2013 / 09:03
1

É difícil fazer isso sem usar um script auxiliar (ou uma função bash) como feito em outra resposta, mas não impossível. Aqui, usando a opção -execdir da expansão do parâmetro find e alguns bash .

find /opt/fooapp/foosubdirectory -name '*.gz' -execdir /bin/bash -c 'pwd ; echo ${0%.gz}; cp ${0} ${0%.gz}.tmp.gz ; gunzip ${0%.gz}.tmp.gz ; mv ${0%.gz}.tmp ${0%.gz}' {} \;

[edit] NOTA: você precisa de uma versão recente de bash (para este parâmetro específico), algumas versões mais antigas não possuem este recurso. Eu testei isso em um V 3.2.x

[Edit] NOTE2: A expressão -execdir , até onde eu sei, está presente no GNU find (e outras implementações modernas), mas não nas mais antigas. Eu testei isso no GNU find v 4.2.x

O mesmo rewitten por legibilidade & comentário:

find /opt/fooapp/foosubdirectory 
     -name '*.gz'
     -execdir /bin/bash -c '_bash_command_string_ ' {} \;
# This ^ will run bash from the subdirectory containing the matched file

  _bash_command_string_  --> 
      pwd ;                       # we are working in this subdir 
      echo ${0%.gz};              # this is matched filename (minus final .gz)
      cp ${0} ${0%.gz}.tmp.gz ;   # copy the .gz file as .tmp.gz
      gunzip ${0%.gz}.tmp.gz ;    # gunzip the .tmp.gz as .tmp
      mv ${0%.gz}.tmp ${0%.gz}    # rename .tmp as matched filename (minus final .gz)

Esta solução é interessante como um hack inteligente, mas provavelmente muito complexo para ser usado na prática.

Veja Referência do Bash - Expansão do Parâmetro do Shell , pesquisa ${parameter%word} .

    
por 09.05.2013 / 16:13
1

Com bash ≥4, execute shopt -s extglob para fazer **/ percorrer diretórios de forma recursiva. (Cuidado que isso atravessa links simbólicos para diretórios. Em zsh, você não precisa de nenhuma configuração especial, e **/ não desce em links simbólicos, mas ***/ faz.) Então, um simples loop é suficiente:

err=
for z in **/*.gz; do
  gunzip <"$z" >"${z%.gz}" &&
  touch -r "$z" "${z%.gz}" ||    # if you want to retain the file's modification time
  err=1
done
if [ -n "$err" ]; then echo >&1 'Watch out, there were errors!'; fi

Com apenas um POSIX sh , invoque um shell a partir de find . É mais difícil extrair o status do erro - verifique se o comando produz alguma coisa no stderr.

find . -name '*.gz' -exec sh -c '
  for z do gunzip <"$z" >"${z%.gz}" && touch -r "$z" "${z%.gz}"; done
' _ {} +
    
por 13.05.2013 / 03:07
0
find . -name \*.gz | parallel gzip -dc {} \> {.}
  • {.} é uma linha de entrada sem extensão
  • O delimitador padrão é apenas linefeed
find . -name \*.gz | while read f; do gzip -dc "$f" > "${f%.gz}"; done
  • Adicione IFS= ou -r se as linhas de entrada puderem iniciar ou terminar com caracteres no IFS ou se puderem conter barras invertidas
por 10.05.2013 / 15:58

Tags