Akankah saya selalu membuat daftar file yang akan dihapus oleh rm?

33

Sesuatu yang saya rasa harus saya ketahui dengan pasti: jika saya ls <something>, akan rm <something>menghapus file yang sama persis yang lsditampilkan? Apakah ada keadaan di mana rmdapat menghapus file yang lstidak muncul? (Ini dalam bash 18,04)

Sunting: terima kasih kepada semua orang yang menjawab. Saya pikir jawaban lengkap adalah kombinasi dari semua jawaban, jadi saya telah menerima jawaban yang paling banyak dipilih sebagai "jawaban".

Hal-hal tak terduga yang saya pelajari selama ini:

  • ls tidak semudah yang Anda kira dalam penanganan argumennya
  • Dalam instalasi sederhana un-fiddled-with Ubuntu, .bashrc alias ls
  • Jangan beri nama file Anda dimulai dengan tanda hubung karena dapat terlihat seperti argumen perintah, dan penamaan satu -r memintanya!
B.Tanner
sumber
8
Saya agak terkejut bahwa rmtidak memiliki --dry-runbendera ...
fkraiem
20
@Rinzwind Mengapa find -deletelebih baik daripada rm? Anda mengatakan "Itu sebabnya" , tetapi sama sekali tidak jelas bagi saya apa yang dimaksud. Juga perhatikan bahwa findpermohonan Anda akan menghapus semua file secara rekursif di direktori saat ini, di mana rmhanya akan menghapus file di direktori langsung. Juga -name *adalah no-op. Secara keseluruhan, saya cukup bingung dengan saran Anda ...
marcelm
3
@marcelm Saya pikir saran untuk menggunakan findini adalah karena Anda dapat menjalankannya, melihat semua file, dan kemudian menjalankan perintah yang sama dengannya -delete. Karena Anda sudah melihat hasil dari find, seharusnya tidak ada ambiguitas dengan apa yang akan dihapus (saya sebenarnya ingin mendengar rincian lebih lanjut tentang ini dalam bentuk jawaban)
Scribblemacher
5
@Scribblemacher "... Anda dapat menjalankannya, melihat semua file, dan kemudian menjalankan perintah yang sama dengan -delete" - Tapi bagaimana itu lebih baik daripada menjalankan ls <filespec>, diikuti oleh rm <filespec>(yang OP sudah tahu bagaimana melakukannya)?
marcelm
12
@Rinzwind "Itu memecahkan jawaban choroba untuk instan di mana file dibuat setelah ls dan sebelum rm." - Tidak, tidak. Jika Anda menjalankan find ... -printpertama untuk mengonfirmasi file apa yang akan dihapus, dan kemudian find ... -delete, Anda masih akan menghapus file yang dibuat di antara kedua perintah. Jika Anda menggunakan keduanya -printdan -delete, Anda tidak mendapatkan konfirmasi, hanya laporan setelah fakta tentang apa yang telah dihapus (dan Anda mungkin juga menggunakannya rm -v).
marcelm

Jawaban:

40

Baik, keduanya lsdan rmberoperasi pada argumen yang diteruskan kepada mereka.

Argumen ini bisa berupa file sederhana, jadi ls file.extdan rm file.extberoperasi pada file yang sama dan hasilnya jelas (daftarkan file / hapus file).

Jika sebaliknya argumen adalah direktori, ls directorydaftarkan konten direktori sementara rm directorytidak akan berfungsi sebagaimana mestinya (yaitu rmtanpa flag tidak dapat menghapus direktori, sementara jika Anda melakukannya rm -r directory, itu secara rekursif menghapus semua file di bawah directory dan direktori itu sendiri ).

Tetapi perlu diingat bahwa argumen baris perintah dapat dikenakan ekspansi shell , jadi tidak selalu dijamin bahwa argumen yang sama diteruskan ke kedua perintah jika mengandung wildcard, variabel, output dari perintah lain, dll.

Sebagai contoh ekstrem pikirkan ls $(rand).txtdan rm $(rand).txt, argumennya "sama" tetapi hasilnya sangat berbeda!

Tuan Shunz
sumber
3
Juga pertimbangkan lsvs di rm *mana ada file "tersembunyi" (dot), meskipun itu sama sekali bukan perbandingan yang adil karena saya tidak menulis ls *. Tetapi rmtidak ada artinya sendiri sehingga semuanya benar-benar apel dan jeruk. Jika saya mengerti dengan benar, itulah inti dari jawaban Anda, pekerjaan yang sangat bagus :)
Lightness Races with Monica
5
Komentar lain pada lsvs rm -r: perintah ls <directory>tidak akan menampilkan file tersembunyi di dalam direktori, tetapi rm -r <directory> akan menghapus bahkan file yang tersembunyi.
Daniel Wagner
1
@LightnessRacesinOrbit lstidak akan mencantumkannya (kecuali jika itu dialihkan ke ls -a), dan rm *tidak akan menghapusnya (kecuali jika Anda telah dotglobmenetapkan).
Stop Harming Monica
20

Jika Anda memikirkan sesuatu seperti ls foo*.txtvs. rm foo*.txt, maka ya, mereka akan menampilkan dan menghapus file yang sama. Shell memperluas glob, dan meneruskannya ke perintah yang dipermasalahkan, dan perintah bekerja pada file yang terdaftar. Satu daftar mereka, satu menghapusnya.

Perbedaan yang jelas adalah bahwa jika ada file-file itu menjadi direktori, maka lsakan daftar isinya, tetapi rmakan gagal untuk menghapusnya. Itu biasanya bukan masalah, karena rmakan menghapus kurang dari apa yang ditunjukkan oleh ls.

Masalah besar di sini berasal dari menjalankan ls *atau rm *dalam direktori yang berisi nama file dimulai dengan tanda hubung . Mereka akan memperluas ke baris perintah dari dua program seolah-olah Anda menuliskannya sendiri, dan lsakan -rberarti "urutan urutan terbalik", sementara yang rmakan -rberarti penghapusan rekursif. Perbedaannya penting jika Anda memiliki subdirektori setidaknya dua level. ( ls *akan menampilkan isi dari direktori level pertama, tetapi rm -r *semuanya akan melewati sublevel pertama juga.)

Untuk menghindarinya, tulis gumpalan permisif dengan ./pengarah untuk menunjukkan direktori saat ini, dan / atau beri --tanda pada akhir pemrosesan opsi sebelum gumpalan (yaitu rm ./*atau rm -- *).

Dengan gumpalan seperti *.txt, itu sebenarnya bukan masalah karena titik adalah karakter opsi yang tidak valid, dan akan menyebabkan kesalahan (sampai seseorang memperluas utilitas untuk menemukan makna untuk itu), tetapi tetap lebih aman untuk meletakkannya di ./sana.


Tentu saja Anda juga bisa mendapatkan hasil yang berbeda untuk kedua perintah jika Anda mengubah opsi globbing shell, atau membuat / memindahkan / menghapus file di antara perintah, tapi saya ragu Anda berarti salah satu dari kasus-kasus itu. (Berurusan dengan file baru / yang dipindahkan akan sangat berantakan untuk dilakukan dengan aman.)

ilkkachu
sumber
1
Terima kasih. Ini sepertinya menyoroti masalah dengan seluruh sintaks baris perintah: jika Anda memberi nama file yang dimulai dengan tanda hubung, Anda sedang berlayar di perairan berbahaya. Siapa yang tahu perintah apa yang mungkin Anda gunakan di masa depan, lama setelah Anda lupa tentang - file.
B.Tanner
1
@ B.Tanner, ya. Dalam arti tertentu, masalahnya adalah bahwa argumen baris perintah hanyalah string biasa. Jika sistem dirancang hari ini, mungkin ada lebih banyak struktur di dalamnya sehingga program yang dieksekusi dapat mengetahui apakah suatu argumen seharusnya menjadi flag opsi atau tidak. Ada juga masalah lain yang berasal dari sangat lemahnya struktur nama file. Ada esai yang sangat menyeluruh tentang hal itu oleh dwheeler , tetapi saya harus memperingatkan bahwa membacanya akan menyakitkan. (Baik dari perincian, atau dari sangat tidak enaknya apa yang salah.)
ilkkachu
3
Saya tidak bisa menolak, Anda telah menjualnya kepada saya, saya pergi untuk membacanya sekarang, dengan kenangan saat saya berhasil mendapatkan karakter carriage return di akhir banyak nama file (mencoba untuk melihat apakah saya bisa membangun file batch Windows yang juga dapat dijalankan sebagai skrip bash ... Saya tidak melihat yang datang!)
B.Tanner
17

Mengesampingkan perilaku shell, mari kita fokus hanya pada apa rmdan lsbisa berurusan dengan diri mereka sendiri. Setidaknya satu kasus di mana lsakan menunjukkan apa yang rmtidak dapat dihapus melibatkan izin direktori, dan yang lainnya - direktori khusus .dan ...

Izin folder

rmadalah operasi pada direktori, karena dengan menghapus file, Anda mengubah isi direktori (atau dengan kata lain daftar entri direktori, karena d irectory tidak lebih dari daftar nama file dan inode ). Ini berarti Anda perlu izin menulis di direktori. Bahkan jika Anda adalah pemilik file , tanpa izin direktori Anda tidak dapat menghapus file. The sebaliknya juga benar : rmdapat menghapus file yang mungkin dimiliki oleh orang lain, jika Anda adalah pemilik direktori.

Jadi, Anda mungkin telah membaca dan mengeksekusi izin pada direktori, yang akan memungkinkan Anda melintasi direktori dan melihat konten dengan baik, misalnya ls /bin/echo, tetapi Anda tidak bisa rm /bin/echokecuali Anda adalah pemilik /bin atau meningkatkan hak istimewa Anda bersama sudo.

Dan Anda akan melihat kasus seperti ini di mana-mana. Inilah satu kasusnya: https://superuser.com/a/331124/418028


Direktori khusus '.' dan '..'

Kasus khusus lainnya adalah .dan ..direktori. Jika Anda melakukannya ls .atau ls .., itu akan dengan senang hati menunjukkan kepada Anda kontennya, tetapi rmmereka tidak diizinkan:

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'
Sergiy Kolodyazhnyy
sumber
15

Jika Anda mengetik ls *dan kemudian rm *, ada kemungkinan Anda akan menghapus lebih banyak file dari yang lsdiperlihatkan - mereka mungkin telah dibuat dalam interval waktu yang sangat kecil antara akhir lsdan awal rm.

choroba
sumber
1
Itu adalah kasus yang mungkin terjadi /tmp, di mana banyak aplikasi dapat membuat file sementara, sehingga selalu ada kemungkinan di kedua perintah dengan *. Namun, beberapa aplikasi juga membuat file anonim dengan unlink()menyimpannya sambil menjaga agar pegangan file tetap terbuka, sehingga file tersebut dapat ditampilkan ls *tetapi rm *tidak dapat ditangkap.
Sergiy Kolodyazhnyy
1
@OrangeDog Anda tidak mengerti intinya. Kita berbicara tentang kondisi balapan
Sergiy Kolodyazhnyy
1
@OrangeDog Saya harus memverifikasi ini karena saya sedang menelepon sekarang, tetapi intinya masih ada walaupun *ada perbedaan antara apa yang lsakan ditampilkan dan apa yang rmberoperasi, karena daftar isi direktori telah berubah di antaranya.
Sergiy Kolodyazhnyy
1
Bahkan jika Anda hanya melakukan satu perintah, ada kondisi perlombaan antara memperluas argumen dan kemudian menghapusnya.
Stop Harming Monica
1
@OrangeDog Saya setuju dengan itu. Karena ekspansi wildcard berfungsi pada nama file yang ada, ya, ada kondisi perlombaan antara shell yang memperluas wildcard, dan perintah yang memprosesnya, jadi ls *sebenarnya bisa menunjukkan nama file yang sudah hilang.
Sergiy Kolodyazhnyy
9

ls *dan rm *tidak bertanggung jawab untuk memperluas gumpalan - yang dilakukan oleh shell sebelum meneruskannya ke perintah.

Ini berarti bahwa Anda dapat menggunakan perintah apa pun dengan daftar file yang diperluas - jadi saya akan menggunakan sesuatu yang sesedikit mungkin.

Jadi cara yang lebih baik untuk melakukan ini (atau setidaknya, cara lain) adalah dengan melewati perantara.

echo *akan menunjukkan kepada Anda apa yang akan diteruskan ke rmperintah Anda .

Bayangan
sumber
2
Terima kasih, saya suka ide menggunakan gema bukannya ls dalam skenario ini.
B.Tanner
3
Atau printf "%s\n" *untuk mendapatkan tampilan yang jelas tentang nama file dengan spasi. (Atau %qsebaliknya untuk berurusan dengan baris baru dan karakter kontrol juga, dengan mengorbankan output yang lebih buruk.)
ilkkachu
4

Bagaimana tentang:

$ mkdir what
$ cd what
$ mkdir -p huh/uhm ./-r
$ ls *
uhm
$ rm *
$ ls
-r
$ ls -R
.:
-r

./-r:

Pada dasarnya wildcard berkembang ke hal-hal yang dimulai dengan -(atau hal-hal yang dimasukkan secara manual dimulai dengan -tetapi yang terlihat sedikit lebih seperti curang) dapat ditafsirkan secara berbeda oleh lsdan rm.


sumber
4

Ada yang kasus di mana tepi apa yang lsmenunjukkan tidak apa rmmenghilangkan. Yang agak ekstrem, tapi untungnya tidak berbahaya adalah jika argumen yang Anda berikan adalah tautan simbolis ke direktori: lsakan menunjukkan kepada Anda semua file di direktori symlink, sementara rmakan menghapus symlink, meninggalkan direktori asli dan isinya tidak tersentuh:

% ln -s $HOME some_link
% ls some_link    # Will display directory contents  
bin    lib    Desktop ...
% rm some_link
% ls $HOME
bin    lib    Desktop ...
alexis
sumber
1
Hah. ln -s $HOME some_link; ls some_linkoutput some_link@untuk saya, tetapi saya telah lsalias ls -F. Rupanya, -Fmengubah perilaku untuk menampilkan tautan alih-alih mendereferensinya. Tidak menyangka itu.
marcelm
Memang! Juga ls -lmisalnya target link, bukan tujuan ... ada harus cara untuk memeriksa link itu sendiri.
alexis
1
Menambahkan opsi ke pertanyaan membuka terlalu banyak kemungkinan - jawaban saya adalah tentang perilaku lsdan rm.
alexis
Jika Anda mengatakan ls some_link/,,  ls -H some_linkatau ls -L some_link, itu akan mendaftar direktori tertaut ke, bahkan jika Anda menambahkan -Fatau -l. Sebaliknya (sort-of), -dmengatakan melihat direktori daripada isinya; bandingkan ls -l /tmpdan ls -ld /tmp.
G-Man Mengatakan 'Reinstate Monica'
Tentu, Anda dapat menambahkan flag yang mengubah perilaku ls. Anda pada dasarnya menunjukkan mengapa menghitung perilaku dengan bendera yang berbeda tidak layak untuk pertanyaan ini ...
alexis
4

Jika Anda hanya melakukan lsalih-alih ls -a, ya rmdapat menghapus file tersembunyi yang belum Anda lihat lstanpa -a.

Contoh:

Menurut :

dir_test
├── .test
└── test2

ls dir_test : hanya akan menampilkan test2

ls -A dir_test : akan menampilkan test2 + .test

rm -r dir_test : akan menghapus semua (.test + test2)

Saya harap itu akan membantu Anda.

DevHugo
sumber
Bisakah Anda memberikan contoh? Karena biasanya, rm *tidak akan menghapus file dot. Jika ya, ls *juga akan menunjukkan kepada mereka.
marcelm
Tidak, ls *jangan tampilkan file tersembunyi.
DevHugo
Tapi ya itu agak bingung, saya menambahkan beberapa contoh.
DevHugo
1
ls -aakan daftar ., .., .testdan test2. Anda mungkin ingin mengubah contoh untuk digunakan ls -A, yang mencantumkan semuanya kecuali . dan ..(yaitu, hanya .testdan test2).
G-Man Mengatakan 'Reinstate Monica'
3

Sudah ada banyak jawaban bagus, tetapi saya ingin menambahkan beberapa wawasan yang lebih dalam.

Tanyakan kepada diri Anda pertanyaan: Berapa banyak parameter dilewatkan ke ls, jika Anda menulis

ls *

...? Perhatikan bahwa lsperintah tidak mendapatkan *parameter sebagai jika ada file yang *dapat diperluas. Sebagai gantinya, shell pertama-tama melakukan globbing sebelum menjalankan perintah, jadi lsperintah sebenarnya mendapatkan banyak parameter karena ada file yang cocok dengan globbing. Untuk menekan globbing, kutip parameternya.

Hal ini berlaku untuk setiap perintah: echo *vs echo '*'.

Ada skrip, panggil saja countparams.shuntuk menguji efeknya. Ini memberi tahu Anda berapa banyak parameter yang dilewati dan mencantumkannya.

#!/bin/bash
echo "This script was given $# parameters."
arr=( "$@" )
for ((i=0;i<$#;i++)); do
        echo "Parameter $((i+1)): ${arr[$i]}"
done

Jadikan itu dapat dijalankan dan dijalankan ./countparams.sh *. Belajar dari hasilnya!

rexkogitans
sumber
1

Glob akan berkembang dengan cara yang sama di kedua kali, jika isi direktori sama pada dua waktu yang berbeda.


Jika Anda benar-benar ingin memeriksa apa yang akan dihapus, gunakan rm -i *.txt. Ini akan meminta Anda secara terpisah untuk setiap file sebelum (mencoba) menghapusnya.

Ini dijamin aman terhadap kondisi balapan:
        ls *.txt/ file baru dibuat / rm *.txt
karena Anda diminta untuk setiap file oleh program yang sama yang melakukan penghapusan.


Ini terlalu rumit untuk penggunaan normal, dan jika Anda rmingin rm -i, Anda akan menemukan diri Anda menggunakan \rmatau rm -fcukup sering. Tetapi perlu setidaknya disebutkan bahwa ada solusi untuk kondisi balapan. (Bahkan portabel untuk sistem non-GNU: POSIX rm(1)menentukan -iopsi .)

Opsi lain adalah bash array:, to_remove=(*.txt)lalu minta pengguna untuk mengonfirmasi (mungkin setelah melakukan ls -ld -- "${to_remove[@]}"), lalu rm -- "${to_remove[@]}". Jadi ekspansi glob hanya dilakukan sekali, dan daftar dilewatkan secara verbatim ke rm.

Opsi praktis lain yang dapat digunakan adalah GNU rm -I( halaman manual ), yang meminta jika menghapus lebih dari 4 item. (Tapi tidak menunjukkan daftar, hanya total.) Saya gunakan alias rm='rm -I'di desktop saya.

Ini adalah perlindungan yang bagus terhadap kembalinya jari gemuk dengan pola setengah mengetik yang terlalu cocok. Tetapi menggunakan yang lspertama umumnya baik di direktori yang Anda miliki, atau pada sistem pengguna tunggal, dan ketika tidak ada proses latar belakang yang secara asinkron dapat membuat file baru di sana. Untuk menjaga agar tidak main jari, jangan mengetik rm -rf /foo/bar/bazdari kiri ke kanan. rm -rf /adalah kasus khusus, tetapi rm -rf /usrtidak! Biarkan -rfbagian itu, atau mulai dengan ls, dan hanya tambahkan rm -rfbagian setelah mengetik lintasan.

Peter Cordes
sumber