Biasanya, jika Anda mengedit skrip, semua penggunaan skrip yang berjalan cenderung mengalami kesalahan.
Sejauh yang saya mengerti, bash (shell lain juga?) Membaca skrip secara bertahap, jadi jika Anda memodifikasi file skrip secara eksternal, ia mulai membaca hal-hal yang salah. Apakah ada cara untuk mencegahnya?
Contoh:
sleep 20
echo test
Jika Anda menjalankan skrip ini, bash akan membaca baris pertama (katakanlah 10 byte) dan pergi tidur. Ketika resume, mungkin ada konten yang berbeda dalam skrip mulai dari byte ke-10. Saya mungkin berada di tengah baris dalam skrip baru. Dengan demikian skrip yang berjalan akan rusak.
\n
akan berhasil? Mungkin subkulit()
akan lakukan? Saya tidak terlalu berpengalaman dengan itu, tolong bantu!sleep 20 ;\n echo test ;\n sleep 20
dan saya mulai menyuntingnya, mungkin akan terjadi kesalahan. Sebagai contoh, bash dapat membaca 10 byte pertama dari skrip, memahamisleep
perintah dan tidur. Setelah dilanjutkan, akan ada konten yang berbeda dalam file mulai dari 10 byte.Jawaban:
Ya, kerang, dan
bash
khususnya, berhati-hati untuk membaca file satu baris pada satu waktu, sehingga berfungsi sama seperti ketika Anda menggunakannya secara interaktif.Anda akan melihat bahwa ketika file tidak dapat dicari (seperti pipa),
bash
bahkan membaca satu byte pada suatu waktu untuk memastikan tidak membaca melewati\n
karakter. Ketika file dicari, itu dioptimalkan dengan membaca blok penuh pada suatu waktu, tetapi mencari kembali setelah\n
.Itu berarti Anda dapat melakukan hal-hal seperti:
Atau tulis skrip yang memperbarui sendiri. Yang tidak akan bisa Anda lakukan jika itu tidak memberi Anda jaminan itu.
Sekarang, jarang sekali Anda ingin melakukan hal-hal seperti itu dan, seperti yang Anda ketahui, fitur itu cenderung lebih sering menghalangi daripada berguna.
Untuk menghindarinya, Anda dapat mencoba dan memastikan Anda tidak mengubah file di tempat (misalnya, memodifikasi salinan, dan memindahkan salinan di tempat (seperti
sed -i
atauperl -pi
dan beberapa editor lakukan misalnya)).Atau Anda dapat menulis skrip Anda seperti:
(perhatikan bahwa penting untuk
exit
berada di jalur yang sama}
; meskipun Anda juga bisa meletakkannya di dalam kawat gigi tepat sebelum yang terakhir).atau:
Shell perlu membaca skrip sampai
exit
sebelum mulai melakukan apa pun. Itu memastikan shell tidak akan membaca dari skrip lagi.Itu berarti seluruh skrip akan disimpan dalam memori.
Itu juga dapat mempengaruhi penguraian skrip.
Misalnya, di
bash
:Akan menampilkan bahwa U + 00E9 dikodekan dalam UTF-8. Namun, jika Anda mengubahnya ke:
The
\ue9
akan diperluas di charset yang berlaku pada saat itu bahwa perintah itu diurai yang dalam hal ini adalah sebelum yangexport
perintah dijalankan.Perhatikan juga bahwa jika perintah
source
alias.
digunakan, dengan beberapa shell, Anda akan memiliki masalah yang sama untuk file-file yang bersumber.Itu tidak terjadi
bash
meskipunsource
perintah yang membaca file sepenuhnya sebelum menafsirkannya. Jika menulis secarabash
spesifik, Anda sebenarnya bisa memanfaatkannya, dengan menambahkan di awal skrip:(Saya tidak akan mengandalkan itu meskipun seperti yang Anda bayangkan versi masa depan
bash
dapat mengubah perilaku yang saat ini dapat dilihat sebagai batasan (bash dan AT&T ksh adalah satu-satunya kerang mirip POSIX yang berperilaku seperti itu sejauh yang bisa diceritakan) danalready_sourced
triknya agak rapuh karena mengasumsikan bahwa variabel tidak ada di lingkungan, belum lagi itu mempengaruhi konten dari variabel BASH_SOURCE)sumber
}; exec true
. Dengan cara ini, tidak ada persyaratan pada baris baru di akhir file, yang ramah untuk beberapa editor (seperti emacs). Semua tes yang saya bisa memikirkan bekerja dengan benar}; exec true
}; exit
? Anda juga kehilangan status keluar.. script
) digunakan.Anda hanya perlu menghapus file (mis. Menyalinnya, menghapusnya, mengganti nama salinan kembali ke nama aslinya). Sebenarnya banyak editor dapat dikonfigurasi untuk melakukan ini untuk Anda. Saat Anda mengedit file dan menyimpan buffer yang diubah, alih-alih menimpa file itu akan mengubah nama file lama, membuat yang baru, dan meletakkan konten baru di file yang baru. Karenanya skrip yang berjalan harus dilanjutkan tanpa masalah.
Dengan menggunakan sistem kontrol versi sederhana seperti RCS yang sudah tersedia untuk vim dan emacs, Anda mendapatkan keuntungan ganda dari memiliki riwayat perubahan Anda, dan sistem checkout secara default harus menghapus file saat ini dan membuatnya kembali dengan mode yang benar. (Hati-hati dengan menautkan file seperti itu tentunya)
sumber
Solusi paling sederhana:
Dengan cara ini, bash akan membaca seluruh
{}
blok sebelum menjalankannya, danexit
arahan akan memastikan tidak ada yang akan dibaca di luar blok kode.Jika Anda tidak ingin "mengeksekusi" skrip, tetapi lebih ke "sumber" itu, Anda memerlukan solusi yang berbeda. Maka ini harus bekerja:
Atau jika Anda ingin kontrol langsung atas kode keluar:
Voa! Skrip ini aman untuk diedit, sumber dan jalankan. Anda masih harus memastikan bahwa Anda tidak memodifikasinya dalam milidetik ketika itu awalnya sedang dibaca.
sumber
Bukti dari konsep. Berikut ini skrip yang memodifikasi sendiri:
kita melihat versi cetak berubah
Ini karena bash memuat membuat pegangan file agar terbuka ke skrip, sehingga perubahan pada file akan segera terlihat.
Jika Anda tidak ingin memperbarui salinan dalam memori, putuskan tautan file asli dan ganti.
Salah satu cara untuk melakukannya adalah dengan menggunakan sed -i.
bukti dari konsep
Jika Anda menggunakan editor untuk mengubah skrip, mengaktifkan fitur "simpan salinan cadangan" mungkin yang diperlukan untuk menyebabkan editor menulis versi yang diubah ke file baru alih-alih menimpa yang sudah ada.
sumber
bash
tidak membuka file denganmmap()
. Hanya berhati-hati untuk membaca satu baris pada satu waktu sesuai kebutuhan, seperti ketika mendapat perintah dari perangkat terminal saat interaktif.Membungkus skrip Anda dalam suatu blok
{}
kemungkinan merupakan opsi terbaik tetapi mengharuskan Anda mengubah skrip Anda.akan menjadi pilihan terbaik kedua (dengan asumsi tmpfs ) kerugiannya adalah $ 0 rusak jika skrip Anda menggunakannya.
menggunakan sesuatu seperti
F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash
ini kurang ideal karena harus menyimpan seluruh file dalam memori dan merusak $ 0.menyentuh file asli harus dihindari sehingga, waktu modifikasi terakhir, kunci baca, dan tautan keras tidak terganggu. dengan cara itu Anda dapat membiarkan editor terbuka saat menjalankan file dan rsync tidak akan perlu checksum file untuk fungsi backup dan hard link seperti yang diharapkan.
mengganti file yang sedang diedit akan berfungsi tetapi kurang kuat karena tidak dapat diterapkan pada skrip lain / pengguna / atau orang mungkin lupa. Dan lagi itu akan memutus tautan keras.
sumber
tac test.sh | tac | bash