Jadi, saya menghapus folder rumah saya (atau, lebih tepatnya, semua file yang saya tulis aksesnya). Apa yang terjadi adalah yang saya miliki
build="build"
...
rm -rf "${build}/"*
...
<do other things with $build>
dalam skrip bash dan, setelah tidak perlu lagi $build
, menghapus deklarasi dan semua penggunaannya - tetapi rm
. Bash dengan senang hati mengembang rm -rf /*
. Ya.
Saya merasa bodoh, menginstal cadangan, redid pekerjaan yang hilang. Mencoba melewati rasa malu.
Sekarang, saya bertanya-tanya: apa teknik untuk menulis skrip bash sehingga kesalahan seperti itu tidak bisa terjadi, atau setidaknya lebih kecil kemungkinannya? Misalnya, sudahkah saya menulis
FileUtils.rm_rf("#{build}/*")
dalam naskah Ruby, penerjemah akan mengeluh karena build
tidak dideklarasikan, jadi bahasa itu melindungi saya.
Apa yang telah saya pertimbangkan dalam bash, selain mengoreksi rm
(yang, sebagaimana banyak jawaban dalam pertanyaan terkait menyebutkan, tidak bermasalah):
rm -rf "./${build}/"*
Itu akan membunuh pekerjaan saya saat ini (repo Git) tetapi tidak ada yang lain.- Varian / parameterisasi
rm
yang memerlukan interaksi ketika bertindak di luar direktori saat ini. (Tidak dapat menemukan.) Efek serupa.
Apakah itu, atau adakah cara lain untuk menulis skrip bash yang "kuat" dalam pengertian ini?
sumber
rm -rf "${build}/*
mana pun tanda kutip pergi.rm -rf "${build}
akan melakukan hal yang sama karenaf
.set -u
tidak mengernyitset -e
, tetapi ia masih memiliki gotcha.#! /usr/bin/env ruby
di bagian atas setiap skrip shell dan lupakan bash;)Jawaban:
atau
Ini akan membuat shell memperlakukan ekspansi variabel yang tidak disetel sebagai kesalahan:
set -u
danset -o nounset
merupakan opsi shell POSIX .Sebuah kosong nilai akan tidak memicu kesalahan sekalipun.
Untuk itu, gunakan
Perluasan
${variable:?word}
akan diperluas ke nilaivariable
kecuali jika itu kosong atau tidak disetel. Jika kosong atau tidak disetel,word
itu akan ditampilkan pada kesalahan standar dan shell akan memperlakukan ekspansi sebagai kesalahan (perintah tidak akan dieksekusi, dan jika berjalan di shell non-interaktif, ini akan berakhir). Membiarkan:
keluar akan memicu kesalahan hanya untuk nilai yang tidak disetel, seperti di bawahset -u
.${variable:?word}
adalah ekspansi parameter POSIX .Tidak satu pun dari ini akan menyebabkan shell interaktif berakhir kecuali
set -e
(atauset -o errexit
) juga berlaku.${variable:?word}
menyebabkan skrip keluar jika variabel kosong atau tidak disetel.set -u
akan menyebabkan skrip keluar jika digunakan bersama denganset -e
.Adapun pertanyaan kedua Anda. Tidak ada cara untuk membatasi
rm
untuk tidak bekerja di luar direktori saat ini.Implementasi GNU
rm
memiliki--one-file-system
opsi yang menghentikannya dari menghapus filesystem yang dipasang secara rekursif, tapi itu sedekat yang saya percaya bisa kita dapatkan tanpa membungkusrm
panggilan dalam fungsi yang benar-benar memeriksa argumen.Sebagai catatan samping:
${build}
persis sama dengan$build
kecuali jika ekspansi terjadi sebagai bagian dari string di mana karakter yang mengikuti berikut adalah karakter yang valid dalam nama variabel, seperti pada"${build}x"
.sumber
${build:?msg}
atau${build?msg}
? 2) Dalam konteks sesuatu seperti membangun skrip, saya pikir menggunakan alat yang berbeda dari rm untuk penghapusan yang lebih aman akan baik-baik saja: kita tahu kita tidak ingin bekerja di luar direktori saat ini, jadi kami membuatnya secara eksplisit dengan menggunakan dibatasi perintah. Tidak perlu membuat rm lebih aman secara umum.:
(itu akan memicu kesalahan hanya untuk variabel yang tidak disetel ). Saya tidak berani mencoba menulis alat yang akan mengatasi menghapus file di bawah jalur tertentu secara eksklusif, dalam semua keadaan. Parsing paths dan caring / not caring tentang tautan simbolik dll. Agak terlalu fiddly untuk saya saat ini.Saya akan menyarankan pemeriksaan validasi normal menggunakan
test
/[ ]
Anda akan aman jika Anda menulis skrip Anda seperti itu:
The
[ -n "${build}" ]
cek yang"${build}"
adalah panjang string non-nol .The
||
adalah logis operator OR di bash. Ini menyebabkan perintah lain dijalankan jika yang pertama gagal.Dengan cara ini, telah
${build}
kosong / tidak terdefinisi / dll. skrip akan keluar (dengan kode pengembalian 1, yang merupakan kesalahan umum).Ini juga akan melindungi Anda jika Anda menghapus semua penggunaan
${build}
karena[ -n "" ]
akan selalu salah.Keuntungan menggunakan
test
/[ ]
adalah ada banyak pemeriksaan lain yang lebih bermakna yang juga dapat digunakan.Sebagai contoh:
sumber
${variable:?word}
diusulkan @Kusalananda?test
(yaitu[
) memiliki banyak pemeriksaan lain yang relevan, seperti-d
(argumennya adalah direktori),-f
(argumennya adalah file),-O
(file tersebut dimiliki oleh pengguna saat ini) dan seterusnya.${build:?word}
? The${variable:?word}
format tidak melindungi Anda jika Anda menghapus semua contoh variabel.if
tidak. 2) "Anda tidak akan menghapus $ {build:? Word} bersamaan dengan itu" - skenario adalah bahwa saya melewatkan penggunaan variabel. Menggunakan${v:?w}
akan melindungi saya dari kerusakan. Seandainya saya menghapus semua penggunaan, bahkan akses biasa tidak akan berbahaya, jelas.Dalam kasus spesifik Anda, saya telah mengerjakan 'penghapusan' di masa lalu untuk memindahkan file / direktori sebagai gantinya (dengan asumsi / tmp berada pada partisi yang sama dengan direktori Anda):
Di belakang layar, ini memindahkan referensi file / dir tingkat atas dari sumber ke
$trashdir
struktur direktori tujuan semua pada partisi yang sama, dan tidak menghabiskan waktu berjalan struktur direktori dan membebaskan blok-blok disk per-file saat itu juga. Ini menghasilkan pembersihan yang jauh lebih cepat ketika sistem sedang digunakan aktif, sebagai ganti untuk reboot yang sedikit lebih lambat (/ tmp dibersihkan pada reboot).Atau, entri cron untuk membersihkan /tmp/.trash-$USER secara berkala akan menjaga / tmp dari pengisian, untuk proses (mis. Build) yang menghabiskan banyak ruang disk. Jika direktori Anda berada di partisi berbeda sebagai / tmp, Anda bisa membuat direktori serupa / tmp di partisi Anda dan memiliki cron clean that gantinya.
Namun yang paling penting, jika Anda mengacaukan variabel dengan cara apa pun, Anda dapat memulihkan konten sebelum pembersihan terjadi.
sumber
/tmp
ada di partisi lain (saya pikir itu selalu untuk saya, setidaknya untuk skrip yang berjalan di ruang pengguna); kemudian, folder tempat sampah perlu diubah (dan tidak akan mendapat untung dari penanganan OS/tmp
).mktemp -d
untuk mendapatkan direktori sementara itu tanpa menginjak-injak jari kaki proses lain (dan untuk menghormati dengan benar$TMPDIR
).Gunakan substitusi bash paramater untuk menetapkan default ketika variabel tidak diinisialisasi, misalnya:
rm -rf $ {variabel: - "/ nonexistent"}
sumber
Saya selalu mencoba memulai skrip Bash saya dengan sebuah baris
#!/bin/bash -ue
.-e
berarti "gagal pada e rror pertama yang tidak disentuh";-u
berarti "gagal pada penggunaan pertama u ndeclared variabel".Temukan lebih banyak detail di artikel hebat Gunakan Mode Ketetapan Bash Tidak Resmi (Kecuali Anda Mengecewakan) . Penulis juga merekomendasikan menggunakan
set -o pipefail; IFS=$'\n\t'
tetapi untuk tujuan saya ini berlebihan.sumber
Saran umum untuk memeriksa apakah variabel Anda ditetapkan, adalah alat yang berguna untuk mencegah masalah semacam ini. Tetapi dalam hal ini ada solusi yang lebih sederhana.
Kemungkinan besar tidak perlu menggumpal isi
$build
direktori untuk menghapusnya tetapi tidak pada$build
direktori itu sendiri. Jadi jika Anda melewatkan extraneous*
maka nilai yang tidak disetel akan berubahrm -rf /
yang secara default sebagian besar implementasi rm dalam dekade terakhir akan menolak untuk melakukan (kecuali jika Anda menonaktifkan perlindungan ini dengan--no-preserve-root
opsi GNU rm ).Melompati trailing
/
juga akan menghasilkanrm ''
pesan kesalahan:Ini berfungsi bahkan jika perintah rm Anda tidak menerapkan perlindungan untuk
/
.sumber
Anda berpikir dalam istilah bahasa pemrograman, tetapi bash adalah bahasa scripting :-) Jadi, gunakan paradigma instruksi yang sama sekali berbeda untuk paradigma bahasa yang sama sekali berbeda .
Pada kasus ini:
Karena
rmdir
akan menolak untuk menghapus direktori yang tidak kosong, Anda harus menghapus anggota terlebih dahulu. Anda tahu apa itu anggota, kan? Itu mungkin parameter untuk skrip Anda, atau berasal dari parameter, jadi:Sekarang, jika Anda meletakkan beberapa file atau direktori lain di sana seperti file temp atau sesuatu yang seharusnya tidak Anda miliki,
rmdir
akan menimbulkan kesalahan. Tangani dengan benar, lalu:Cara berpikir ini telah membantu saya dengan baik karena ... ya, sudah ada, melakukan itu.
sumber
make clean
akan menggunakan beberapa wildcard (kecuali jika kompiler yang sangat rajin membuat daftar setiap file yang dibuatnya). Juga, bagi saya tampaknyarm -rf ${build}/${parameter}
hanya sedikit memindahkan masalah.make
pertanyaan itu. Menulis jawaban yang hanya dapat diterapkanmake
akan menjadi asumsi yang sangat spesifik dan mengejutkan. Jawaban saya berfungsi untuk kasus selainmake
, selain pengembangan perangkat lunak, selain masalah khusus pemula yang Anda alami dengan menghapus barang-barang Anda.