Como "extrair" um arquivo zip?

52

Eu extraí um arquivo zip em uma pasta não vazia. O arquivo zip tem muitos arquivos e uma hierarquia profunda, que foi mesclada com a árvore existente do diretório de destino. Como posso remover os arquivos e diretórios que foram criados descompactando sem destruir os arquivos e diretórios que já estavam lá? Obviamente, ainda tenho o arquivo zip em que me mesclei, então as informações estão lá.

mafp
fonte
Umm obrigado pela aceitação, mas foi realmente a idéia do @ jjin. Eu não estava ciente das lqopções para unzizp, apenas adicionei alguns truques clássicos * nix em torno de sua resposta principal.
terdon
Tudo bem, eu realmente não me importo muito. Eu adicionei minha própria versão diferente de manipulação de espaço em branco de qualquer maneira.
jjlin
@terdon Sim ... também votei na resposta de jjlin, mas só posso aceitar uma resposta.
Mafp
Para referência futura, sempre siga um destes procedimentos com um arquivo desconhecido de qualquer formato: 1) Extraia-o para um diretório vazio ou 2) Liste-o primeiro (descompacte -l) antes de extraí-lo para poder ver se é desagradável assim. Arquivos criados sem um diretório de nível superior com tudo que está em mau estado. Quando feito com alcatrão, eles são realmente chamados de bombas de alcatrão, então eu acho que isso poderia ser chamado de bomba compactada.
19413 Joe
@ Joe Ele tem seus usos. Pacotes LaTeX, por exemplo, podem vir em um foo.tds.zipformulário. Esses zips se fundem em uma árvore TEXMF, o que é muito conveniente. Mas se você quiser remover um pacote desse tipo, terá o problema que descrevi.
Mafp

Respostas:

28

A resposta de jjlin é o caminho a percorrer. Eu só quero adicionar algumas opções para diretórios:

  • Exclua todos os arquivos extraídos , sem diretórios :

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
  • Excluir somente arquivos extraídos e diretórios vazios

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done; rmdir *

    Sem opções, rmdirexclui apenas diretórios vazios, deixando apenas arquivos e pastas não vazias para que você possa executá-lo com segurança *.

  • Exclua tudo extraído, mas solicite uma confirmação antes de cada exclusão:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -ri "$n"; done; rmdir *

    O -isinalizador fará com rmque seja solicitado antes de cada remoção. Você pode escolher Sim ou Não.

  • Exclua tudo extraído, diretórios incluídos:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -rf "$n"; done
Terdon
fonte
A exclusão de diretórios vazios é fácil com find: find * -depth -type d -exec rmdir {} +e ignora todas as Directory not emptymensagens. Pode ser legal encurtar isso para find * -type d -deletea -deleteopção ativada, -depthmas não verifiquei que -deletenão excluirá um diretório não vazio.
Adrian Pronk
@AdrianPronk não:find: cannot delete './foo': Directory not empty
terdon
28

Você pode usar unzip -lqq <filename.zip>para listar o conteúdo do arquivo zip; isso incluirá algumas informações estranhas que você precisará filtrar. Aqui está um comando que funciona para mim:

unzip -lqq file.zip | awk '{print $4;}' | xargs rm -rf

O awkcomando extrai apenas os nomes dos arquivos e diretórios. Em seguida, o resultado é passado xargspara excluir tudo. Sugiro executar um comando a seco (ou seja, omitindo a xargs rm -rfparte) primeiro para garantir que os resultados estejam corretos.

O comando acima terá problemas ao lidar com caminhos com espaço em branco. Esta versão (mais complicada) deve corrigir isso:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf
jjlin
fonte
Isso já está muito próximo do que eu tinha em mente, mas unzip -lqqlista também os diretórios contidos no zip. Por enquanto, deixaria todos os diretórios em paz. Como excluir todos os diretórios vazios de uma árvore pode ser uma pergunta de acompanhamento.
Mafp
@mafp Esse é um bom argumento sobre os diretórios. Você pode adicionar grep -v '/$'ao pipeline para ignorar a exclusão dos diretórios (todos com uma barra final, AFAICT).
jjlin
@terdon Na verdade, acho que o problema começa no awk, pois imprimir apenas US $ 4 não imprime o caminho completo.
jjlin
Eu não acho que você deva usar a -ropção rm: isso parece estar causando problemas, especialmente quando combinado com a -fopção. Eu não usaria a -fopção nesse cenário.
Adrian Pronk
11
@jjlin: omitirá grep -v '/$'apenas as entradas do diretório no arquivo ZIP. Eles ainda incluirão entradas que eram arquivos simples no arquivo ZIP, mas eram diretórios preexistentes na pasta de destino. Por esta razão, seria prudente omitir-r
Adrian Pronk
11

Com a opção -Z1, descompactar listará exatamente um arquivo por linha (e nada mais).

Dessa forma, você pode usar

unzip -Z1 | xargs -I {} rm '{}'

para excluir todos os arquivos extraídos do arquivo zip.

O comando

unzip -Z1 | xargs -I {} rm -rf '{}'

excluirá diretórios também, mas você deve ter cuidado. Se os diretórios já existiam antes de extrair o arquivo zip, todos os arquivos pré-existentes nesses diretórios também serão excluídos.


Se você quiser extrair o arquivo zip de qualquer maneira, há outra abordagem garantida para lidar com nomes de arquivos estranhos.

Primeiro, extraia o arquivo zip onde você originalmente pretendia extraí-lo:

unzip file.zip -d elsewhere

Agora, mude para o diretório em que você extraiu os arquivos por engano e execute o seguinte comando:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f encontra apenas arquivos (sem diretórios).

  • %P\0é o caminho relativo (sem elsewhere/), seguido por um caractere nulo.

  • -0faz xargs separar linhas por caracteres nulos. Isso é mais confiável, pois, em teoria, os nomes de arquivos podem conter caracteres de nova linha.


Para lidar com os diretórios restantes, você pode executar o comando:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d encontra apenas diretórios.

  • -exec rmdir -p {} \;executa rmdir -p {}para todos os diretórios encontrados.

    {}é o diretório que foi encontrado e a -popção faz com que o rmdir remova seus diretórios-pai vazios também.

  • 2> /dev/null suprime as mensagens de erro que surgirão ao tentar excluir diretórios não vazios ou excluídos anteriormente.


Páginas de manual relacionadas:

Dennis
fonte
+1 por me fazer ler zipinfoa página de manual.
terdon
Bem, isso torna um pouco mais fácil. :)
jjlin
2

Aqui está uma solução ainda mais fácil e segura (eu acho)

zip -m getmeoutofhere.zip `unzip -lqq myoriginalzipfile.zip`
rm getmeoutofhere.zip

O que isto está fazendo: O comando descompactar aspas posteriores produzirá uma lista do que estava no seu arquivo original.

O zip -m usará essa lista para adicionar e adicionar cada um deles a getmehereofhere.zip e removê-lo do diretório original (portanto, teoricamente, isso deve ser um indicativo para myoriginalfile.zip.

A desvantagem é que descompactar -lqq produzirá algum texto extra, datas, horas, tamanho do arquivo etc. Isso fará com que o zip -m produza mensagens de erro, mas isso não terá efeito (a menos que você tenha o caso improvável de um arquivo com o mesmo nome).

Observe que isso não removerá nenhum diretório criado durante o descompactação original.

David E.
fonte
Abordagem interessante, irá explorar mais.
MAFP
1

Se você extraiu os arquivos de modo que o registro de data e hora da modificação no arquivo morto não seja preservado nas cópias extraídas (mas os arquivos extraídos têm seu tempo de modificação usual), a maneira correta de atacar isso é através do tempo de modificação. Todos os arquivos extraídos têm um carimbo de data / hora de modificação mais recente que o arquivo existente modificado mais recentemente nesse diretório.

Aqui está uma situação simples.

Suponha que nenhum dos arquivos existentes no diretório atual tenha sido tocado por pelo menos 24 horas. Qualquer coisa que foi modificada nas últimas 24 horas é, portanto, lixo do arquivo zip.

$ find . -mtime -1 -print0 | xargs -0 rm

Isso também encontrará alguns diretórios, mas rmos deixará em paz. Eles podem ser tratados em um segundo passe:

$ find . -mtime 1 -type d -print 0 | xargs -0 rmdir

Todos os diretórios que foram modificados recentemente foram modificados pelo zip. Se rmdirremovê-los com sucesso, isso significa que eles estão vazios. Diretórios vazios que foram tocados pelo zip provavelmente foram criados por ele: ou seja, vieram do arquivo morto. Não podemos ter 100% de certeza. É possível que o trabalho de descompactação coloque alguns arquivos em um diretório existente que estava vazio.

Se finda granularidade de 24 horas não for boa o suficiente para o trabalho, porque os arquivos na árvore foram modificados muito recentemente, então considerarei algo simples: suponha que o trabalho de descompactação não coloque nada nos subdiretórios existentes. Ou seja, tudo o que foi descompactado é um arquivo no nível superior ou um novo subdiretório que não existia antes e, portanto, não contém nada além do material do zip. Então:

# list directory in descending order of modification time
$ ls -1t > filelist  # descending order of modification time

Agora, abrimos filelistem um editor de texto e determinamos a primeira entrada na lista que não veio do zip. Excluímos essa entrada e tudo mais depois dela. O que resta são os arquivos e diretórios que vieram do zip. Primeiro, inspecionamos visualmente questões como espaços nos nomes e ocorrências de aspas que precisam ser escapadas. Em seguida, podemos adicionar aspas a tudo, se necessário: O seguinte pressupõe que você usa o Vim:

:%s/.*/"&"/

Em seguida, junte tudo em uma grande linha:

:%j

Agora insira rm -rfna frente dele:

Irm - rf<ESC>

Execute a linha sob o cursor como um comando shell:

!!sh<Enter>

Definitivamente, eu não automatizaria as etapas desta tarefa, devido ao risco de apagar arquivos que já estavam lá ou estragar tudo devido a problemas de nome de arquivo.

Se você for o caminho óbvio para obter uma lista dos caminhos no zip, capture-o em um arquivo, examine-o com muito cuidado e transforme-o em uma remoção após fazer a edição necessária.

Kaz
fonte