Bir zip dosyasını nasıl “ayıklamak”?


52

Bir zip dosyasını boş olmayan bir klasöre çıkarttım. Zip dosyası, hedef dizinin mevcut ağacıyla birleştirilmiş çok sayıda dosyaya ve derin bir hiyerarşiye sahiptir. Zaten açılmış olan dosyaları ve dizinleri yok etmeden açarak oluşturduğu dosyaları ve dizinleri nasıl kaldırabilirim? Tabii ki, hala birleştirdiğim zip dosyası var, bu yüzden bilgi var.


Kabul ettiğiniz için teşekkürler, ama bu gerçekten @ jjin'in fikriydi. lqSeçeneklerinin farkında unzizpdeğildim, ana cevabının etrafına bazı klasik * nix numaraları ekledim.
terdon

Sorun değil, pek umrumda değil. Zaten kendi boşluk işleme yöntemimi de ekledim.
jjlin

@terdon Evet ... Ben de jjlin'in cevabını değiştirdim, ama sadece bir cevabı kabul edebilirim.
mafp

Gelecekte başvurmak için, her zaman herhangi bir formatta yabancı bir arşiv ile aşağıdakilerden birini yapın: 1) Boş bir dizine çıkartın veya 2) Çıkarmadan önce ilk önce onu (unzip -l) listeleyin, böylece böyle kötü olup olmadığını görebilirsiniz. Üst düzey bir dizin olmadan yapılan arşivler, altındaki her şey kötüdür. Katran ile yapıldığında, aslında katran bombaları denir, bu yüzden buna zip bombası denebilir.
Joe,

@Joe Kullanımları vardır. LaTeX paketleri, örneğin, bir foo.tds.zipbiçimde gelebilir . Bu fermuarlar çok uygun bir TEXMF ağacında birleşir. Ancak böyle bir paketi kaldırmak istiyorsanız, tarif ettiğim sorunla yüzleşiyorsunuz.
mafp

Yanıtlar:


28

jjlin 'in cevabı gitme yoludur. Sadece dizinler için birkaç seçenek eklemek istiyorum:

  • Çıkarılan tüm dosyaları silin , dizin yok :

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
  • Çıkarılan dosyaları ve yalnızca boş dizinleri silin

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

    Hiçbir seçenek olmadığında, rmdiryalnızca boş dizinleri siler, dosyaları ve boş olmayan klasörleri yalnız bırakarak güvenle çalıştırabilirsiniz *.

  • Çıkarılan her şeyi silin , ancak her silme işleminden önce bir onay isteyin:

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

    -iBayrak neden olacaktır rmher kaldırılmadan önce sorulması için, size Evet veya Hayır seçebilir

  • Sil herşeyi çıkarılan, dizinleri dahil:

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

Boş klasörlerin silinmesi ile kolayca yapılır find: find * -depth -type d -exec rmdir {} +ve tüm Directory not emptymesajları dikkate almaz . O bu kısaltmak için yasal olabilir find * -type d -deleteolarak -deleteseçenek açılır -depthama olmadığı henüz doğrulanmamıştır -deleteboş olmayan bir dizini silmez.
Adrian Pronk

@AdrianPronk öyle değil:find: cannot delete './foo': Directory not empty
terdon

28

unzip -lqq <filename.zip>Zip dosyasının içeriğini listelemek için kullanabilirsiniz ; Bu olsa da, filtrelemek gerekecek bazı yabancı bilgiler içerecektir. İşte benim için çalışan bir komut:

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

awkKomut özü dosya ve dizinleri sadece adları. Sonra sonuç xargsher şeyi silmek için geçer . xargs rm -rfSonuçların doğru olduğundan emin olmak için önce komutun kuru çalışmasını (yani, parçanın çıkarılması) yapılmasını öneririm .

Yukarıdaki komut, boşluk içeren yollarla ilgili sorunlara sahip olacaktır. Bu (daha karmaşık) sürüm, şunu düzeltmelidir:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf

Bu zaten aklımdaki şeye oldukça yakın, fakat unzip -lqqaynı zamanda fermuardaki dizinleri de listeliyor. Şimdilik, tüm dizinlerin yalnız kalmasına izin verirdim. Bir ağaçtaki tüm boş dizinlerin nasıl silineceği bir takip sorusu olabilir.
mafp

@mafp Dizinler için iyi bir nokta. grep -v '/$'Dizinleri silmeyi atlamak için boru hattına ekleyebilirsiniz (hepsi AMAICT.
jjlin

@ terdon Aslında sorunun başladığını düşünüyorum awk, çünkü sadece 4 dolarlık baskı tam yolu basmayacak .
jjlin

-rRm seçeneğini kullanmanız gerektiğini düşünmüyorum : bu, özellikle -fseçenekle birlikte kullanıldığında sorun istiyor gibi görünüyor . -fBu senaryoda bu seçeneği hiç kullanmazdım .
Adrian Pronk

1
@jjlin: grep -v '/$'sadece ZIP dosyasındaki dizin girişlerini çıkarır . Yine de ZIP dosyasında düz dosyalar olan ancak hedef klasörde önceden var olan dizinler olan girişleri içereceklerdir. Bu nedenle, ihmal etmek akıllıca olacaktır-r
Adrian Pronk,

11

Anahtar -Z1ile unzip, satır başına tam olarak bir dosya (ve başka bir şey değil) listeleyecektir.

Bu şekilde kullanabilirsiniz

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

Zip dosyasından çıkartılan tüm dosyaları silmek için

Komuta

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

dizinleri de siler, ancak dikkatli olmanız gerekir. Zip dosyası çıkarılmadan önce dizinler önceden mevcutsa, bu dizinlerde önceden var olan tüm dosyalar da silinir.


Zip dosyasını yine de ayıklayacaksanız, garip dosya adlarıyla başa çıkmanın garantisi olan başka bir yaklaşım daha var.

Öncelikle, sıkıştırılmış dosyayı çıkartmak istediğiniz yere çıkartın:

unzip file.zip -d elsewhere

Şimdi, dosyaları yanlışlıkla çıkardığınız dizine geçin ve aşağıdaki komutu uygulayın:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f sadece dosyaları bulur (dizin yok).

  • %P\0göreceli yol (olmadan elsewhere/), ardından boş bir karakterdir.

  • -0xargs'ı satırları boş karakterlerle ayırır. Bu daha güvenilirdir, çünkü teoride dosya isimleri yeni satır karakterleri içerebilir.


Artık dizinlerle uğraşmak için şu komutu uygulayabilirsiniz:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d sadece dizinleri bulur.

  • -exec rmdir -p {} \;rmdir -p {}Bulunan her dizin için çalıştırır .

    {}bulunan dizindir ve -panahtar, rmdir'in boş üst dizinlerini de kaldırmasını sağlar.

  • 2> /dev/null boş olmayan veya daha önce silinen dizinleri silmeye çalışmaktan kaynaklanan hata mesajlarını gizler.


İlgili adam sayfaları:


zipinfo'In man sayfasını okumam için +1 .
terdon

Tanrım, bu biraz daha kolay. :)
jjlin

2

İşte daha kolay ve daha güvenli (bence) bir çözüm

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

Bu ne yapıyor: backquoted unzip komutu orijinal dosyanızdakilerin bir listesini üretecektir.

zip -m daha sonra bu listeyi kullanarak her birini getmeoutofhere.zip dosyasına ekleyin ve orijinal dizinden kaldırın (bu nedenle teorik olarak myoriginalfile.zip'e bağımlı olmalıdır).

Dezavantajı, unzip -lqq dosyasının bazı ekstra metinler, tarihler, zamanlar, dosya boyutu vb. Üretmesidir . Bunlar zip -m'nin hata mesajları üretmesine neden olur, ancak bunun bir etkisi olmamalıdır (aynı dosyaya sahip olma ihtimaline sahip olmadıkça) ) adlandırın.

Bunun, orijinal unzip sırasında oluşturulan dizinleri kaldırmayacağını lütfen unutmayın.


İlginç bir yaklaşım daha da keşfedecek.
mafp

1

Dosyaları, arşivdeki değişiklik zaman damgasının ayıklanan kopyalarda korunmadığı şekilde çıkardıysanız (ancak ayıklanan dosyalar normal değişiklik zamanına sahiptir), buna saldırmanın doğru yolu değişiklik zamanıdır. Çıkarılan tüm dosyalar, bu dizinde en son değiştirilen mevcut dosyadan daha yeni bir değişiklik zaman damgasına sahiptir.

İşte basit bir durum.

Geçerli dizindeki mevcut dosyalardan hiçbirine en az 24 saat boyunca dokunulmadığını varsayalım. Son 24 saatte değiştirilen herhangi bir şey, bu nedenle zipfile'den önemsizdir.

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

Bu da bazı dizinleri bulacak, fakat rmonları yalnız bırakacak. İkinci geçişte ele alınabilir:

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

Yakın zamanda değiştirilen dizinler zip tarafından değiştirildi. Eğer rmdirbunları başarılı kaldırır, bu onların boş olduğu anlamına gelir. Zip tarafından dokunulan boş dizinler büyük olasılıkla yarattı: yani arşivden geldi. % 100 emin olamayız. Sıkıştırma işinin, bazı dosyaları boş olan varolan bir dizine koyması mümkündür.

Eğer findağaç dosyaları çok son değiştirilen çünkü 'ın 24 saat ayrıntı, bu iş için yeterince iyi değil, o zaman ben bir sonraki basit bir şey düşünün: unzip iş varolan alt dizinleri içine bir şey koymadı varsayalım. Başka bir deyişle, sıkıştırılmamış her şey en üst düzeydeki bir dosya veya daha önce orada olmayan yeni bir alt dizindir, bu nedenle zipten malzeme içeren hiçbir şey içermez. Sonra:

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

Şimdi filelistbir metin editöründe açtık ve listeden ilk gelenleri zip'den gelmedik. Bu girişi ve ondan sonraki her şeyi sileriz. Geriye kalan, zip'ten gelen dosya ve dizinlerdir. Öncelikle isimlerdeki boşluklar ve kaçılması gereken tekliflerin oluşumları gibi konuları görsel olarak inceliyoruz. Gerekirse her şeyin etrafına tırnak işaretleri ekleyebiliriz: Aşağıdakiler Vim kullandığınızı varsayar:

:%s/.*/"&"/

O zaman hepsine büyük bir çizgide katılın:

:%j

Şimdi rm -rfönüne yerleştirin :

Irm - rf<ESC>

İmlecin altındaki satırı bir kabuk komutu olarak çalıştırın:

!!sh<Enter>

Kesinlikle, zaten var olan dosyaları silme ya da dosya adı sorunları nedeniyle batma riski nedeniyle bu görevin adımlarını otomatikleştiremem.

Zip içindeki yolların bir listesini alma yoluna gidecekseniz, daha sonra bir dosyaya kaydedin, çok dikkatli bir şekilde inceleyin ve gerekli düzenlemeleri yaptıktan sonra kaldırmaya dönüştürün.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.