Hapus file, tetapi hanya jika itu symlink

11

Idealnya saya ingin perintah seperti ini

rm --only-if-symlink link-to-file

karena saya telah membakar diri saya berkali-kali secara tidak sengaja menghapus file bukannya symlink yang menunjuk ke file. Ini bisa sangat buruk ketika sudo terlibat. Sekarang saya tentu saja melakukan ls -aluntuk memastikan itu benar-benar symlink dan semacamnya tetapi itu rentan terhadap kesalahan operator (sama-sama bernama file, kesalahan ketik, dll) dan kondisi balapan (jika seseorang ingin saya menghapus file karena suatu alasan). Apakah ada cara untuk memeriksa apakah suatu file adalah symlink dan hanya menghapusnya jika ada dalam satu perintah?

Shelvacu
sumber
1
Saya tidak berpikir bahwa kondisi balapan harus menjadi perhatian. Siapa pun yang dapat menghapus symlink dan menggantinya dengan file akan memerlukan izin tertulis pada direktori, yang berarti mereka juga dapat menghapus file itu sendiri.
Nate Eldredge

Jawaban:

19
 $ rm_if_link(){ [ ! -L "$1" ] || rm -v "$1"; }

 #test
 $ touch nonlink; ln -s link
 $ rm_if_link nonlink
 $ rm_if_link link
   removed 'link'     
PSkocik
sumber
4
Tentunya akan lebih jelas untuk menghapus !dan mengubah ||ke&&
glenn jackman
5
@glennjackman Saya sudah terbiasa memilih ||formulir. &&membawa segalanya jika Anda dalam set -emode.
PSkocik
@PSkocik Secara umum berisiko karena tidak membedakan antara case yang menarik (file ada dan merupakan symlink) dan alasan lain untuk kode keluar yang tidak nol, seperti nilai tanda kutip kosong yang kosong ( [ ! -L $1 ]), menggunakan operator yang tidak valid ( [ ! -l foo ]), atau bahkan kesalahan sintaksis ( [ ! -q foo || echo foo)
l0b0
2
@XiongChiamiov Masalahnya rm_if_link(){ [ -L "$1" ] && rm -v "$1"; }adalah menjalankannya pada set -e mode nonlink akan mematikan proses shell Anda. Anda bisa menambahkan || :tetapi kemudian menjadi, IMHO, bahkan lebih tidak jelas daripada ! ||versi, dan itu akan mencegah rmkegagalan membunuh Anda set -e, yang kemungkinan tidak diinginkan. ! ||cukup ringkas dan jelas, dan tidak menderita salah satu dari masalah ini.
PSkocik
1
Tidak . Opsi lain: a) letakkan bagian rmdalam tes Anda, b) gunakan pernyataan if aktual, c) jangan berkeliling menggunakan -eshell interaktif (dan uji skrip Anda!). Jika Anda ingin memperdebatkan tentang keterbacaan, gunakan ifuntuk menguji apakah ada tautan yang merupakan pemenangnya.
Boikot SE untuk Monica Cellio
5

Anda dapat menggunakan finddan -type lkondisi pengujiannya (yang menguji untuk melihat apakah objek yang ditemukan adalah tinta l atau tidak)

Misalnya, jika Anda memiliki file yang dipanggil foodi direktori saat ini, Anda bisa melakukan ini:

$ find . -type l -iname "foo" -delete

Anda mungkin dapat menyederhanakannya hanya dengan:

$ find . -maxdepth 1 -type l -delete

Yang akan menghapus semua symlink di direktori saat ini.

Peringatan:

The -deletepilihan di findbenar-benar benar-benar berbahaya. PASTIKAN UNTUK MENEMPATKANNYA DI AKHIR FIND COMMAND Jika Anda salah menaruhnya, itu akan menghapus semua yang ditemukan terlepas dari apakah hasilnya cocok dengan kondisi Anda.

Seperti yang disarankan dalam komentar, opsi yang lebih aman adalah menggunakan finddan rm -i(yang memaksa Anda mengonfirmasi penghapusan file) bersamaan:

$ $ find . -type l -iname "foo" | xargs rm -i

Secara pribadi saya gunakan -exec trash {} \;untuk menghapus file sementara, karena saya, seperti Anda sendiri, telah dibakar rmsebelumnya. Double berlaku untuk -deletebendera yang salah tempat .

http://slackermedia.ml/trashy

Klaatu von Schlacker
sumber
1
Alih-alih menghapus, Anda hanya dapat mencetaknya dan meneruskannya ke xargs: find . -type l -iname "foo" | xargs rm -i lebih aman.
Boban P.
1
Saya suka menggunakan @BobanP. rm -iUntuk keamanan, pasti.
Klaatu von Schlacker
3
"Ini akan menghapus semua symlink di direktori saat ini" ... dan titik di bawahnya.
Joseph R.
1
Sebagai @ JosephFR. kata, itu akan pergi jauh-jauh ke jalan. Tambahkan -maxdepth 1 di find.
Boban P.
1
Masih memecah nama file dengan spasi. Sarankan menggunakan -print0untuk finddan -0untuk xargs.
Wildcard
4
zsh -c 'rm foo(@)'

@adalah kualifikasi glob ; polanya foo(@)cocok dengan yang foococok, tetapi hanya tautan simbolik.

foo(-@)hanya akan cocok dengan tautan simbolis yang rusak. foo(@,L0)hanya akan cocok dengan tautan simbolis dan file kosong.

Tentu saja, jika Anda menjalankan zsh di tempat pertama, Anda hanya perlu mengetik rm foo(@). Anda harus berhati-hati untuk tidak menekan Entersebelum mengetik (@).

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
Zsh terdengar semakin kuat ketika saya melihat jawaban seperti ini.
Jeff Schaller