Dosyaları silmek için rm $ (ls) kullanmanın herhangi bir dezavantajı var mı?


13

rm $(ls)Dosyaları silmek için (ya da rm -r $(ls)dizinleri de silmek için) kullanarak güvenli olup olmadığını merak ediyordum ? Çünkü tüm web sitelerinde, bu komut diğer komutlardan çok daha kolay görünse de insanlar bunu yapmak için başka yollar sunar.


3
Temel cevap, hayır. Özel karakterleri tutamaz. Bunu daha ayrıntılı olarak açıklamak için biraz cevap yazabilirim
Sergiy Kolodyazhnyy

5
touch 'foo -r .. bar'; rm $(ls); Ana dizinim nereye gitti? Ayrıca, bundan daha karmaşık olan ne tür alternatifler görüyorsunuz? rm *yazmak ve düşünmek çok daha kolaydır ve daha güvenlidir (ama tamamen güvenli değildir; bkz. Dennis'in cevabı).
Peter Cordes

1
Mükemmel cevaplara ek olarak, lsuygulamalar arasında değişiklik gösterebileceğini ve bu nedenle standart olmadığını unutmayın. İhtiyacınız olan şeye bağlı olarak, findve gibi alternatifleri göz önünde bulundurun stat. Sen kullanmalıdır lsasla diğer komutlar veya komut kullanımı için, sadece insan tüketimi için.
Paddy Landau

Yanıtlar:


7

Bunun amacı nedir?

  • ls geçerli dizindeki dosyaları listeler
  • $(ls)lsargüman olarak yerlerin çıktısını değiştirirrm
  • Esasen rm $(ls)mevcut dizindeki tüm dosyaları silmeyi amaçlamaktadır

Bu resimde yanlış olan ne ?

lsdosya adındaki özel karakterleri düzgün işleyemiyor. Unix kullanıcıları genellikle farklı yaklaşımlar kullanmanızı tavsiye eder . Ayrıca dosya adlarını saymayla ilgili bir soruda da gösterdim . Örneğin:

$ touch file$'\n'name                                                                                                    
$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ 

Ayrıca, Denis'in cevabında gerektiği gibi belirtildiği gibi, önde gelen tire işaretleri olan bir dosya adı, dosya adının rmkaldırılması amacını yenen ikame sonrası argüman olarak yorumlanabilir .

Ne çalışıyor

Geçerli dizindeki dosyaları silmek istiyorsunuz. Bu yüzden glob kullanın rm *:

$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ rm *
$ ls
$ 

findKomutu kullanabilirsiniz . Bu araç sık sık geçerli dizinden daha fazlası için önerilir - dizin ağacının tamamını tekrar tekrar kullanabilir ve dosyalar üzerinde-exec . . .{} \;

$ touch "file name"                                
$ find . -maxdepth 1 -mindepth 1                                                                                         
./file name
$ find . -maxdepth 1 -mindepth 1 -exec rm {} \;                                                                          
$ ls
$ 

Python'un dosya adlarında özel karakterlerle sorunu yoktur, bu yüzden bunu da kullanabiliriz (bunun sadece dosyalar için olduğunu, kullanmanız os.rmdir()ve os.path.isdir()dizinlerde çalışmak istiyorsanız unutmayın):

python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

Aslında, yukarıdaki komut ~/.bashrckısalık için işlev veya takma ad haline dönüştürülebilir . Örneğin,

rm_stuff()
{
    # Clears all files in the current working directory
    python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

}

Bunun Perl versiyonu

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'

1
Sizin "$(ls)"dizinde sadece bir dosya varsa örnek çalışır. Tek tamamlama olduğundan dosya adını genişletmek için sekme tamamlamayı da kullanabilirsiniz.
Peter Cordes

@PeterCordes, garip bir nedenle sadece bir dosya ile çalışır. O lszaman kullanmaya karşı bir argüman daha :) Bunu düzenleyeceğim
Sergiy Kolodyazhnyy

Buna garip bir neden demem: $(ls)kelime bölmeyi devre dışı bırakmak için teklif verirsiniz veya kelime bölmenin gerçekleşmesine izin verirsiniz (feci sonuçlarla). Verileri kod olarak ele almadan birden fazla dizenin bir listesini geçirmenin tek temiz yolu dizi değişkenleriyle veya \0ayırıcı olarak kullanmaktır, ancak kabuğun kendisi bunu yapamaz. Hala IFS=$'\n'en az tehlikeli, ama eşleşemez find -print0 | xargs -0. Veya grep -l --null. Veya gibi şeylerle tüm sorunu önlemek find -exec rm {} +. ( +Her rm çağrısına birden çok argümanı iletmeyi unutmayın ; YOL daha verimli).
Peter Cordes

@PeterCordes Yup, orada tamamen aynı fikirdeydi. Ancak IFS=$'\n'bu durumda da başarısız olacak, çünkü dosya adında yeni satırım var, bu nedenle kelime bölüştürme onu bir yerine iki dosya adı olarak ele alacak. Ancak garip neden, IFSboşluk, sekme, satırsonu, orijinal olan varsayılan ile rm "$(ls)"de başarısız olması, dosya adını iki ayrı olan olarak söylediğim gibi davranması gerektiğidir, ancak olmadı. Tipik kullandığım findile -execveya find . . .-print0 | while IFS= read -d'' FILENAME ; do . . . donedosya adları ile başa çıkmak için yapının. Ya da biri kullanabilir python, buna bir örnek ekledim.
Sergiy Kolodyazhnyy

"$(ls)"her zaman tek bir argümana genişler, çünkü tırnaklar $(ls)kelime bölünmesini engeller. Tıpkı korudukları gibi "$foo".
Peter Cordes

26

Hayır, güvenli değildir ve yaygın olarak kullanılan alternatif rm *çok daha güvenli değildir.

İle ilgili birçok sorun var rm $(ls). Diğerleri yanıtlarında daha önce yer aldıkları gibi, çıktıları iç alan ayırıcısındals bulunan karakterlere bölünecektir .

En iyi senaryo, işe yaramaz. En kötü senaryoda, yalnızca dosyaları (ancak dizinleri değil) kaldırmayı ya da bazı dosyaları seçerek kaldırmayı amaçladınız -i- ancak c -rfgeçerli dizinde adı olan bir dosya var . Bakalım ne olacak.

$ mkdir a
$ touch b
$ touch 'c -rf'
$ rm -i $(ls)
$ ls
c -rf

Komutun rm -i $(ls)yalnızca dosyaları kaldırması ve her birini kaldırmadan önce sorması gerekiyordu, ancak sonuçta yürütülen komut okundu

rm -i a b c -rf

bu yüzden tamamen başka bir şey yaptı.

Bunun rm *sadece marjinal olarak daha iyi olduğunu unutmayın . Daha önce olduğu gibi dizin yapısı ile, burada amaçlandığı gibi davranacaktır, ancak adlı bir dosyanız -rfvarsa, hala şansınız kalmaz .

$ mkdir a
$ touch b
$ touch ./-rf
$ rm -i *
$ ls
-rf

Birkaç daha iyi alternatif var. En kolay olanlar sadece rm ve globbing içerir.

  • Komuta

    rm -- *
    

    tam olarak amaçlandığı gibi çalışacaktır, burada --ondan sonraki her şeyin bir seçenek olarak yorumlanmaması gerektiğine işaret eder.

    Bu, yirmi yılı aşkın süredir POSIX yardımcı programı sözdizimi yönergelerinin bir parçası olmuştur. Yaygın, ancak her yerde mevcut olmasını beklememelisiniz.

  • Komuta

    rm ./*
    

    globun farklı şekilde genişlemesini sağlar ve bu nedenle çağrılan yardımcı programdan destek gerektirmez.

    Yukarıdaki örneğim için, nihayetinde echo bekleyen tarafından yürütülecek komutu görebilirsiniz .

    $ echo rm ./*
    rm ./a ./b ./-rf
    

    Başta rm , seçenek adı gibi dosya adlarından herhangi birine yanlışlıkla davranılmasını./ önler .


1
Çok iyi bir nokta, genişleme ile dosya adları - sonra bayrak haline gelir rm. +1
Sergiy Kolodyazhnyy

1
Hatta nastier: touch 'foo -rf .. bar. Çıktısında bir yol ayırıcı üretemedikçe, bir saldırganın üst dizinden daha yüksek olabileceğini sanmıyorum ls.
Peter Cordes

@Peter saldırganının ilk önce patent dizinini kaldırmak için yazma iznine sahip olması gerekir, değil mi?
Sergiy Kolodyazhnyy

1
@PeterCordes Tüm rm sürümlerinin bu arızaya sahip olup olmadığından emin değilim , ancak Ubuntu, openSUSE ve Fedora'da, rm: refusing to remove '.' or '..' directory: skipping '..'üst dizini kaldırmaya çalışırken buna benzer bir şey söylüyor .
Dennis

@Serg: Saldırgan size sadece bu dosya adıyla bir .zip gönderir ve ayıklayarak ve ardından içeriği silmeye çalışarak kendinizi vurmanıza izin verir. Veya / var / tmp dosyasında bu dosya adını oluşturarak.
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.