Find ile belirli bir alt dizin dışındaki tüm dosyaları sil


11

aAlt klasördeki tüm dosyalar dışında , klasörde bir süre erişilmeyen tüm dosyaları özyinelemeli olarak silmek istiyorum b.

find a \( -name b -prune \) -o -type f -delete

Ancak, bir hata iletisi alıyorum:

find: -delete eylemi -depth'i otomatik olarak açar, ancak -depth yürürlükte olduğunda -prune hiçbir şey yapmaz. Yine de devam etmek istiyorsanız, sadece açıkça -depth seçeneğini kullanın.

Ekleme -depthnedenlerini tüm dosyaları bolmalıdır dahil edilecek, değil olur.

Bu işi yapmanın güvenli bir yolunu bilen var mı?


@ MichaelKjörling: Extglob'a bir göz attım, ama ahariç her şeyi nasıl ekliyorsunuz a/b?
forthrin

Olmaz cd a && ls -d !(b/*)işe? (Sadece bunu yapmak için rm -ryerine ls -d.)
Bir CVn

Öneriniz alt klasörleri siler. Klasörleri olduğu gibi tutmak istiyorum. (Altındaki dosyalar hariç ) altındaki ağaçtaki tüm dosyaları bulmak ve silmek istiyorum . aa/b
18th

Yani sadece rm'ye atlayın -r. Sorduğunuz şey, bash'ın genişletilmiş globbing'i kullanarak oldukça kolay bir şekilde cevaplanıyor gibi görünüyor ve daha sonra globbing sonucuyla yaptığınız şey size kalmış.
CVn

@ MichaelKjörling Bu iki sorunun belirsiz bir şekilde benzer çözümlere sahip olması, soruları tekrarlamıyor. Her iki sorunun çözümlerinin çoğu diğer sorunu çözmez.
Gilles 'SO- kötü olmayı bırak'

Yanıtlar:


13

TL; DR: En iyi yol -exec rmyerine kullanmaktır -delete.

find a \( -name b -prune \) -o -type f -exec rm {} +

Açıklama:

Neden kullanmaya çalıştığınızda şikayet buluyor -deleteile -prune?

Kısa cevap: çünkü -deleteima eder -depthve etkisiz -depthkılar -prune.

Uzun cevaba gelmeden önce, önce ve olmadan bulma davranışını gözlemleyin -depth:

$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2

Tek bir dizindeki siparişle ilgili hiçbir garanti yoktur. Ancak bir dizinin içeriğinden önce işleneceğine dair bir garanti vardır. foo/Herhangi birinden önce foo/*ve foo/barönce not edin foo/bar/*.

Bu tersine çevrilebilir -depth.

$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/

Şimdi hepsinin foo/*daha önce göründüğünü unutmayın foo/. İle aynı foo/bar.

Daha uzun cevap:

  • -prunebulgunun bir dizine inmesini engeller. Başka bir deyişle -prune, dizinin içeriğini atlar. Sizin durumunuzda -name b -prunebulgunun adıyla herhangi bir dizine inmesini engeller b.
  • -depthdizinin içeriğini dizinin kendisinden önce işlemek için bulur. Bu, find'ın dizin girdisini işlemeye başlamasıyla b, içeriğinin zaten işlendiği anlamına gelir. Dolayısıyla -prune, -depthetkisiyle etkisizdir .
  • -deleteima -deptho zaman boş bir dizin ilk dosyaları silmek ve böylece. -deleteboş olmayan dizinleri silmeyi reddeder. Sanırım -deleteboş olmayan dizinleri silmek ve / veya -deleteima etmeyi önlemek için bir seçenek eklemek mümkün olurdu -depth. Ama bu başka bir hikaye.

İstediğinizi başarmanın başka bir yolu var:

find a -not -path "*/b*" -type f -delete

Bunu hatırlamak daha kolay olabilir veya olmayabilir.

Bu komut yine de dizine iner ve içindeki bher dosyayı yalnızca -notreddetmek için işler . Dizin bçok büyükse bu bir performans sorunu olabilir .

-pathfarklı çalışır -name. tüm yolla eşleşirken -nameyalnızca dosya veya dizinin adıyla eşleşir -path. Örneğin yolu inceleyin /home/lesmana/foo/bar. -name -bareşleşeceğinden addır bar. -path "*/foo*"dize /fooyolda olduğu için eşleşecektir . -pathkullanmadan önce anlamanız gereken bazı karmaşıklıklar vardır. findDaha fazla ayrıntı için sayfasının kılavuz sayfasını okuyun .

Bunun% 100 kusursuz olmadığına dikkat edin. "Yanlış pozitif" olma şansı vardır. Komutun yukarıda yazılma şekli, adın başladığı herhangi bir üst dizine b(pozitif) sahip olan herhangi bir dosyayı atlayacaktır . Ancak b, ağaçtaki konumdan bağımsız olarak adın başladığı tüm dosyaları atlar (yanlış pozitif). Bu, daha iyi bir ifade yazarak düzeltilebilir "*/b*". Bu okuyucu için bir alıştırma olarak kaldı.

Ben kullandığınız varsayalım ave bdaha fazlası gibi tutucuları ve gerçek isimler olarak allosaurusve brachiosaurus. Eğer brachiosaurusyerine koyarsanız b, yanlış pozitif miktarı büyük ölçüde azalacaktır.

En azından yanlış pozitif olacak değil o trajik olarak değil olacak, böylece silindi. Ayrıca, ilk önce komutu çalıştırmadan -delete(ancak ima edileni yerleştirmeyi unutmayın -depth) ve çıktıyı inceleyerek yanlış pozitifleri kontrol edebilirsiniz .

find a -not -path "*/b*" -type f -depth

-not -pathsadece bir şeydi! Cömert bir açıklama için teşekkürler!
forthrin

1
Niçin -not -pathişe yaradığına dair bazı ayrıntılar -pruneyardımcı olacaktır. Neden -not -pathbirlikte var olabilir -depth?
Faheem Mitha

3

Sadece kullanmak rmyerine -delete:

find a -name b -prune -o -type f -exec rm -f {} +

1
Neden rmişe yarayıp yaramadığını açıklayabilir misiniz delete?
Faheem Mitha

1
Oh, sanırım belki de "-delete boş olmayan dizinleri silmeyi reddediyor.", @Lesmana alıntılamak için. Bu yüzden boş olmayan dizinleri silmeyi reddediyor. Ama rmbu problemi yok. Ancak, ne olursa olsun, detaylandırma iyi bir şey olacaktır.
Faheem Mitha

@FaheemMitha, bunun cevabı soruda. Açıkçası çalışamayacağı -deleteanlamına gelir . çalışır, ancak keşfetmesi gerekmeyen dizinlere inmek için durmaz. -depth-prune-pathfind
Stéphane Chazelas

0

Yukarıdaki cevaplar ve açıklamalar çok yardımcı oldu.

"-Exec rm {} +" veya "-not -path ... -delete 'geçici çözümlerini kullanıyorum, ancak bunlar" find ... -delete "ifadesinden çok daha yavaş olabilir. -delete "NFS dosya sistemindeki derin dizinlerde" -exec rm {} + "değerinden 5 kat daha hızlı çalıştır.

'-Not yolu' çözümü, hariç tutulan dizinlerdeki ve altındaki tüm dosyalara bakmanın bariz yüküne sahiptir.

"Find .. -exec rm {} +" sistem çağrılarını yapan rm'yi çağırır:

fstatat(AT_FDCWD, path...); 
unlinkat(AT_FDCWD, path, 0)

"Find -delete" sistem çağrılarını yapar:

 fd=open(dir,...);
 fchdir(fd); 
 fstatat(AT_FDCWD, filename,...)
 unlinkat(dirfd, filename,...)

Bu nedenle "-exec rm {} +" rm komutu, dosya başına iki kez inode aramasına giden tam yolu yapar, ancak "find -delete", geçerli dizindeki dosya adının stat ve bağlantısını kaldırır. Bir dizindeki birçok dosyayı kaldırdığınızda bu büyük bir kazançtır.

(mızmızlanma modu açık (üzgünüm))

Görünüşe göre, -depth, -delete ve -prune arasındaki etkileşimin tasarımı, ortak eylemi "-prune dizinleri dışındaki dosyaları sil" i yapmanın en etkili yolunu gereksiz yere ortadan kaldırıyor.

"-Type f -delete" birleşimi, dizinleri silmeye çalışmadığı için -depth olmadan çalışabilmelidir. Alternatif olarak, "find" ifadesinin dizinleri silmediğini söyleyen bir "-deletefile" eylemi varsa, -depth'in ima edilmesi gerekmez.

Eğer rm dosya adlarını sıralamak, dizinleri açmak ve tam yolların bağlantısını kaldırmak yerine unlinkat (dir_fd, dosya adı) yapmak için bir seçenek varsa xargs veya find -exec çağrılarını rm komutu hızlandırabilir. -R seçeneği ile dizinler arasında özyineleme yaparken zaten unlinkat (dir_fd, dosyaadı) yapar.

(whine modu kapalı)

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.