adlarında \ n karakteri olan klasörlerde döngü için


9

Ben \nonların adı ile karakter bazı klasörler var .

Örneğin:

$ ls
''$'\n''Test'

Bu, Test adı ve adından önce boş bir satırı olan bir klasöre başvurur.

Bu nedenle, üst dizininde böyle bazı komut dosyaları çalıştırdığımda:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

Gösteriyor:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

Klasör adının iki satırı olduğu için iki kez, biri açık \ndiğeri açık olarak çalıştığı için Test.

Peki bu sorunu nasıl çözebilirim, script \nTestsadece bir klasör olduğunu biliyor mu?


3
-print0Find yönergesini ve -dokuma seçeneğini kullanmanız gerekir . Bkz. Stackoverflow.com/a/40189667/7552
Glennman Jackman

1
@glennjackman Lütfen cevap verin!
tatlı

@glennjackman Yanıtınız için teşekkür ederiz, ancak find * -type d -print0 | while IFS= read -d '' file ; do rmdir $file ; donekomut bu çıktıya sahiptir rmdir: failed to remove 'Test': No such file or directory.
Tara S Volpe

5
Sen gerekir değişkeni alıntı:rmdir "$file"
Glenn Jackman

1
@glennjackman Bunu cevap olarak gönderin. Bu uygun bir çözüm ve yorumların yanı sıra bunun için en iyi yer değil. Zaten ima etti :)
Sergiy Kolodyazhnyy

Yanıtlar:


12

Orada sadece tek bir komut var, bu yüzden bayrak çağrısı findile aramak yeterli :-execrmdir

find -depth -type d -exec rmdir {} \;

Veya -deleteseçeneği olduğu gibi kullanın find -type d -delete, ancak boş olmayan dizinlerle çalışmaz. Bunun için -emptybayrağa da ihtiyacınız olacak . Not Ayrıca, -deleteima -deptho Atlanan olabilir böylece. Böylece her şeyi tek bir süreç olarak tutan bir başka uygulanabilir alternatif:

find -type d -empty -delete

Dizin boş değilse kullanın rm -rf {} \;. Sadece dizinleri izole etmek için \ndosya biz bash en birleştirebilirsiniz ANSI-C alıntı $'...' ile -nameopption:

find  -type d -name $'*\n*' -empty -delete

POSIX-ly, bu şekilde halledebiliriz:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

Hedefiniz dizinlerin kaldırılması ise, o -deletezaman yeterli olduğunu, ancak dizinde bir komut yürütmek istiyorsanız, o -execzaman en uygun olduğunu belirtmek gerekir .

Ayrıca bakınız


2
... kullanım rm -rf özenle . ; P btw seçeneği findyok mu -delete? Boş dizinleri siler ve boş olmayanlar için hata atar rmdir- daha az pahalı olabilir.
tatlı

3
@dessert Yapar ve ekledim, ancak -deleteboş olmayan dizinleri kaldırmayacağım. Bu nedenle, dikkatli kullanımırm -rf
Sergiy Kolodyazhnyy

6
Her zaman kuru işletilen find(yani kaldırmak herhangi yıkıcı yük olmadan komutları -deleteveya -exec whatever \;etkilenen dosyaların liste ... aslında doğru olduğunu ve silinen almak gerekir şeyler içermiyorsa kontrol etmek)
Byte Komutanı

2
Bu askubuntu.com olduğundan GNU olduğunu varsayabiliriz find. -exec rmdir {} +Birden fazla argümanı bir rmdirkomut satırına toplu yapmak için kullanın . Ve BTW " ama boş olmayan dizinlerle çalışmaz. " İçin de geçerlidir rmdir, bu aslında bir fark değildir -delete. Bu bir fark rm -r.
Peter Cordes

2
find -type d -empty -delete( -deleteima eder -depth).
Kevin

10

Bunun yerine kabuk globları kullanabilirsiniz find:

for d in */ ; do 
    rmdir "$d"
done

Kabuk glob */, geçerli dizindeki tüm klasörlerle eşleşir. Bu for-loop yapısı, otomatik olarak doğru kelime bölünmesine dikkat eder.

Kabuk seçeneklerinize bağlı olarak, bunun gizli klasörleri (a ile başlayan ad) yok sayabileceğini unutmayın. Bu davranış, komut kullanılarak geçerli oturumdaki tüm dosyalarla eşleşecek şekilde değiştirilebilir shopt -s dotglob.

Ayrıca değişkenlerinizi her zaman alıntılamayı unutmayın.


Glob sadece yinelemeli gibi geçerli dizinde dosyaları / dizinleri bulacaksınız findyapar
ilkkachu

1
Haklısın @ilkkachu. Bu, globstar kabuk seçeneğini etkinleştirerek shopt -s globstarve **/*/bunun yerine bir glob kullanarak değiştirilebilir .
Bayt Komutanı

6

Şimdiye kadar yazılan her iki cevap da rmdirdizin başına bir kez çağırıyor , ancak rmdirbirden fazla argümanı alabileceği gibi merak ediyorum: Daha etkili bir yol yok mu?

Biri basitçe yapabilirdi

rmdir */

ve bu kesinlikle en kolay ve en etkili yoldur, ancak birçok dizin durumunda hata verebilir (bkz . gnome terminalindeki komut satırı argümanlarının maksimum uzunluğu nedir? ). Bu yaklaşımın yinelemeli olarak çalışmasını istiyorsanız globstarkabuk seçeneğini ile etkinleştirin shopt -s globstarve **/*/yerine kullanın */.

GNU ile find(ve sadece kullanmak istemiyorsak -delete)

find -depth -type d -exec rmdir {} +

komut satırını “ xargskomut satırlarını oluşturanla aynı şekilde” oluşturur ( man find). Yedek -depthiçin -maxdepth 1bunu yinelemeli çalışmak istemiyorsanız.

Bu cevapta üçüncü ve IMO'nun parlak bir yolu steeldriver tarafından açıklanmaktadır :

printf '%s\0' */ | xargs -0 rmdir

Bu, printfsıfır sınırlamalı bir argüman listesi oluşturmak için shell yerleşimini kullanır , daha sonra bu liste tam olarak gerektiği kadar xargsçağrı yapan borulara bağlanır rmdir. Yukarıdaki gibi shopt -s globstarve **/*/bunun yerine yinelemeli olarak çalışmasını sağlayabilirsiniz */.


2
findözyinelemeli, ancak OP'nin döngüsü değil. Bu davranışı çoğaltmak için kullanın find -maxdepth 1 -type d -exec rmdir {} +. ( -depthbu durumda gereksizdir.) printfTaklit etmek için düzgün bir hile, daha find -print0önce görmemiştim.
Peter Cordes

1
Ah! Gördüm lsve whiledöngü, onlar bir süreç ikamesi gelen yönlendirme olduğunu cevapsız findyerine boru lsdöngüye ve sadece nedense bunu göstermiyor. Yani find *gerçekten gömüldü. Evet, özyinelemeli ve dizinde çok fazla dizin girdisi varsa (dizinler de dahil olmak üzere kullanmadığı için başarısız olur) */. find .çok daha iyi olurdu, çünkü ing'in bash'ın glob genişlemesinden daha findhızlı stat.
Peter Cordes
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.