Saya punya pertanyaan umum, yang mungkin merupakan hasil dari kesalahpahaman tentang bagaimana proses ditangani di Linux.
Untuk tujuan saya, saya akan mendefinisikan 'skrip' sebagai potongan kode bash yang disimpan ke file teks dengan izin eksekusi yang diaktifkan untuk pengguna saat ini.
Saya memiliki serangkaian skrip yang saling memanggil secara bersamaan. Demi kesederhanaan, saya akan memanggil mereka skrip A, B, dan C. Script A melakukan serangkaian pernyataan dan kemudian berhenti, kemudian mengeksekusi skrip B, kemudian berhenti, kemudian mengeksekusi skrip C. Dengan kata lain, seri langkah-langkahnya adalah seperti ini:
Jalankan Skrip A:
- Serangkaian pernyataan
- Jeda
- Jalankan Script B
- Jeda
- Jalankan Script C
Saya tahu dari pengalaman bahwa jika saya menjalankan skrip A hingga jeda pertama, kemudian melakukan pengeditan di skrip B, pengeditan tersebut tercermin dalam pelaksanaan kode ketika saya mengizinkannya untuk melanjutkan. Demikian juga jika saya mengedit skrip C sementara skrip A masih dijeda, kemudian mengizinkannya untuk melanjutkan setelah menyimpan perubahan, perubahan itu tercermin dalam pelaksanaan kode.
Inilah pertanyaan sebenarnya, apakah ada cara untuk mengedit Script A saat masih berjalan? Atau apakah pengeditan tidak mungkin setelah eksekusi dimulai?
sumber
Jawaban:
Di Unix, kebanyakan editor bekerja dengan membuat file sementara baru yang berisi konten yang diedit. Ketika file yang diedit disimpan, file asli dihapus dan file sementara berganti nama menjadi nama asli. (Ada, tentu saja, berbagai perlindungan untuk mencegah dataloss.) Ini, misalnya, gaya yang digunakan oleh
sed
atauperl
ketika dipanggil dengan-i
bendera ("di tempat"), yang sama sekali tidak "di tempat". Seharusnya disebut "tempat baru dengan nama lama".Ini berfungsi dengan baik karena unix menjamin (setidaknya untuk sistem file lokal) bahwa file yang dibuka terus ada sampai ditutup, bahkan jika itu "dihapus" dan file baru dengan nama yang sama dibuat. (Bukan kebetulan bahwa panggilan sistem unix untuk "menghapus" file sebenarnya disebut "batalkan tautan".) Jadi, secara umum, jika penerjemah shell memiliki beberapa file sumber terbuka, dan Anda "mengedit" file dengan cara yang dijelaskan di atas , shell bahkan tidak akan melihat perubahan karena masih membuka file asli.
[Catatan: seperti halnya semua komentar berbasis standar, di atas tunduk pada beberapa interpretasi dan ada berbagai kasus sudut, seperti NFS. Pedant dipersilakan untuk mengisi komentar dengan pengecualian.]
Tentu saja dimungkinkan untuk memodifikasi file secara langsung; itu hanya sangat tidak nyaman untuk keperluan pengeditan, karena sementara Anda dapat menimpa data dalam file, Anda tidak dapat menghapus atau menyisipkan tanpa menggeser semua data berikut, yang akan menyiratkan banyak menulis ulang. Selain itu, ketika Anda melakukan pemindahan itu, konten file tidak dapat diprediksi dan proses yang membuat file terbuka akan menderita. Untuk menghindari hal ini (seperti halnya sistem basis data, misalnya), Anda memerlukan seperangkat protokol modifikasi yang canggih dan kunci yang didistribusikan; hal-hal yang jauh di luar cakupan utilitas pengeditan file yang khas.
Jadi, jika Anda ingin mengedit file saat sedang diproses oleh shell, Anda memiliki dua opsi:
Anda dapat menambahkan ke file. Ini harus selalu berhasil.
Anda dapat menimpa file dengan konten baru dengan panjang yang persis sama . Ini mungkin atau mungkin tidak berfungsi, tergantung pada apakah shell sudah membaca bagian file atau tidak. Karena sebagian besar file I / O melibatkan buffer baca, dan karena semua shell yang saya tahu membaca seluruh perintah majemuk sebelum menjalankannya, sangat tidak mungkin Anda bisa lolos dengan ini. Tentu tidak akan bisa diandalkan.
Saya tidak tahu adanya kata-kata dalam standar Posix yang sebenarnya membutuhkan kemungkinan untuk menambahkan ke file skrip saat file sedang dieksekusi, sehingga mungkin tidak bekerja dengan setiap shell yang sesuai dengan Posix, apalagi dengan penawaran saat ini dari hampir dan kadang-kadang shell yang sesuai dengan posix. Jadi YMMV. Tapi sejauh yang saya tahu, itu berfungsi baik dengan bash.
Sebagai bukti, inilah implementasi "bebas-loop" dari program bir 99 botol terkenal di bash, yang digunakan
dd
untuk menimpa dan menambahkan (penimpaan itu mungkin aman karena menggantikan garis yang saat ini sedang dijalankan, yang selalu merupakan baris terakhir dari file, dengan komentar yang persis sama panjangnya; saya melakukan itu sehingga hasil akhirnya dapat dieksekusi tanpa perilaku modifikasi diri.)sumber
export beer=100
sebelum menjalankan skrip, itu berfungsi seperti yang diharapkan.bash
berjalan jauh untuk memastikan itu membaca perintah sebelum menjalankannya.Misalnya di:
Shell akan membaca skrip dengan blok, sehingga kemungkinan membaca kedua perintah, menafsirkan yang pertama dan kemudian mencari kembali ke akhir
cmd1
skrip dan membaca skrip lagi untuk membacacmd2
dan menjalankannya.Anda dapat dengan mudah memverifikasi:
(Meskipun melihat
strace
output pada itu, tampaknya ia melakukan beberapa hal yang lebih mewah (seperti membaca data beberapa kali, mencari kembali ...) daripada ketika saya mencoba yang sama beberapa tahun yang lalu, jadi pernyataan saya di atas tentang lseeking kembali mungkin tidak berlaku lagi pada versi yang lebih baru).Namun jika Anda menulis skrip Anda sebagai:
Shell harus membaca hingga penutup
}
, simpan di memori dan jalankan. Karena ituexit
, shell tidak akan membaca dari skrip lagi sehingga Anda dapat mengeditnya dengan aman ketika shell menafsirkannya.Atau, saat mengedit skrip, pastikan Anda menulis salinan skrip baru. Shell akan terus membaca yang asli (bahkan jika itu dihapus atau diganti namanya).
Untuk melakukannya, ubah nama
the-script
menjadithe-script.old
dan salinthe-script.old
kethe-script
dan edit.sumber
Sebenarnya tidak ada cara aman untuk memodifikasi skrip saat sedang berjalan karena shell dapat menggunakan buffering untuk membaca file. Selain itu, jika skrip dimodifikasi dengan menggantinya dengan file baru, shell biasanya hanya akan membaca file baru setelah melakukan operasi tertentu.
Seringkali, ketika skrip diubah saat dieksekusi, shell akhirnya melaporkan kesalahan sintaksis. Ini disebabkan oleh fakta bahwa, ketika shell menutup dan membuka kembali file skrip, ia menggunakan byte offset ke dalam file untuk memposisikan dirinya kembali.
sumber
Anda bisa menyiasatinya dengan memasang jebakan pada skrip Anda, dan kemudian menggunakan
exec
untuk mengambil konten skrip baru. Namun perhatikan,exec
panggilan memulai skrip dari awal dan bukan dari tempat skrip tersebut telah berjalan dalam proses yang sedang berjalan, dan skrip B akan dipanggil (seterusnya).Ini akan terus menampilkan tanggal di layar. Saya kemudian dapat mengedit skrip saya dan mengubahnya
date
menjadiecho "Date: $(date)"
. Saat menulis itu, skrip yang berjalan masih hanya menampilkan tanggal. Bagaimana jika saya mengirim sinyal yang saya tetapkantrap
untuk ditangkap, skrip akanexec
(menggantikan proses yang sedang berjalan dengan perintah yang ditentukan) yang merupakan perintah$CMD
dan argumen$@
. Anda dapat melakukan ini dengan mengeluarkankill -1 PID
- di mana PID adalah PID dari skrip yang sedang berjalan - dan output berubah untuk ditampilkanDate:
sebelumdate
output perintah.Anda dapat menyimpan "keadaan" skrip Anda dalam file eksternal (dalam say / tmp), dan membaca konten untuk mengetahui di mana "melanjutkan" pada saat program dieksekusi kembali. Anda kemudian dapat menambahkan penghentian perangkap tambahan (SIGINT / SIGQUIT / SIGKILL / SIGTERM) untuk menghapus file tmp itu sehingga ketika Anda memulai kembali setelah menyela "Script A" itu akan mulai dari awal. Versi stateful akan seperti:
sumber
$0
dan$@
pada awal skrip dan menggunakan variabel-variabel tersebut sebagaiexec
gantinya.