Bagaimana Linux menangani skrip shell?

22

Untuk pertanyaan ini, mari kita pertimbangkan skrip shell bash, meskipun pertanyaan ini harus berlaku untuk semua jenis skrip shell.

Ketika seseorang mengeksekusi skrip shell, apakah Linux memuat semua skrip sekaligus (ke dalam memori mungkin) atau apakah ia membaca perintah skrip satu per satu (baris demi baris)?

Dengan kata lain, jika saya menjalankan skrip shell dan menghapusnya sebelum eksekusi selesai, apakah eksekusi akan dihentikan atau apakah akan berlanjut seperti itu?

Pengguna Terdaftar
sumber
3
Cobalah. (Ini akan berlanjut.)
devnull
1
@ Devnull sebenarnya ada pertanyaan menarik di sini. Memang, apakah akan melanjutkan atau tidak sepele untuk menguji tetapi ada perbedaan antara file biner (yang dimuat ke dalam memori) dan skrip dengan garis shebang, atau skrip tanpa garis shebang.
terdon
1
Anda mungkin tertarik dengan jawaban ini
terdon
23
Untuk maksud maksud Anda yang sebenarnya, menghapus skrip shell selama eksekusi, tidak masalah apakah itu dibaca sekaligus atau baris demi baris. Di Unix, sebuah inode tidak benar-benar dihapus (bahkan jika tidak ada tautan ke sana dari direktori mana pun) sampai file terakhir yang dibuka ditutup. Dengan kata lain, bahkan jika shell Anda membaca baris script shell demi baris selama eksekusi, masih aman untuk menghapusnya. Satu-satunya pengecualian adalah jika shell Anda adalah jenis yang menutup dan membuka kembali script shell setiap kali, tetapi jika itu terjadi, Anda memiliki masalah (keamanan) yang jauh lebih besar.
Chris Jester-Young

Jawaban:

33

Jika Anda menggunakan, straceAnda dapat melihat bagaimana skrip shell dijalankan ketika dijalankan.

Contoh

Katakanlah saya punya skrip shell ini.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Menjalankannya menggunakan strace:

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

Melihat ke dalam strace.logfile mengungkapkan hal berikut.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Setelah file dibaca, file itu dieksekusi:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

Pada contoh di atas kita dapat dengan jelas melihat bahwa seluruh skrip tampaknya sedang dibaca sebagai satu kesatuan, dan kemudian dieksekusi di sana setelahnya. Jadi itu akan "muncul" setidaknya dalam kasus Bash bahwa ia membaca file, dan kemudian menjalankannya. Jadi, Anda akan berpikir Anda dapat mengedit skrip saat sedang berjalan?

CATATAN: Namun, jangan! Baca terus untuk memahami mengapa Anda tidak boleh main-main dengan file skrip yang berjalan.

Bagaimana dengan penerjemah lain?

Tapi pertanyaan Anda agak tidak jelas. Bukan Linux yang seharusnya memuat konten file, itu adalah penerjemah yang memuat konten, jadi terserah bagaimana penerjemah mengimplementasikan apakah itu memuat file seluruhnya atau dalam blok atau baris sekaligus.

Jadi mengapa kita tidak bisa mengedit file?

Jika Anda menggunakan skrip yang jauh lebih besar namun Anda akan melihat bahwa tes di atas agak menyesatkan. Bahkan sebagian besar penerjemah memuat file mereka dalam blok. Ini cukup standar dengan banyak alat Unix tempat mereka memuat blok file, memprosesnya, dan kemudian memuat blok lain. Anda dapat melihat perilaku ini dengan T&J T&J yang saya tulis beberapa waktu lalu mengenai grep, berjudul: Berapa banyak teks yang dikonsumsi grep / egrep setiap kali? .

Contoh

Katakanlah kita membuat skrip shell berikut.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

Menghasilkan file ini:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Yang berisi jenis konten berikut:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Sekarang ketika Anda menjalankan ini menggunakan teknik yang sama di atas dengan strace:

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

Anda akan melihat bahwa file sedang dibaca dengan kenaikan 8KB, jadi Bash dan shell lainnya kemungkinan tidak akan memuat file secara keseluruhan, melainkan membacanya dalam bentuk blok.

Referensi

slm
sumber
@terdon - ya saya ingat pernah melihat T&J sebelumnya.
slm
5
Dengan skrip 40-byte, tentu saja, itu terbaca dalam satu blok. Coba dengan skrip> 8kB.
Gilles 'SANGAT berhenti menjadi jahat'
Saya tidak pernah mencoba, tetapi saya pikir menghapus file tidak benar-benar dilakukan sampai semua proses menutup deskriptor file yang terkait dengan file yang dihapus, jadi bash mungkin melanjutkan membaca dari file yang dihapus.
Farid Nouri Neshat
@Gilles - ya saya menambahkan contoh, sudah sampai ke sana.
slm
2
Perilaku ini tergantung pada versi. Saya menguji dengan versi bash 3.2.51 (1) -release, dan menemukan bahwa itu tidak buffer melewati garis saat ini (lihat jawaban stackoverflow ini ).
Gordon Davisson
11

Ini lebih tergantung shell daripada tergantung OS.

Tergantung pada versinya, kshbaca skrip on demand dengan blok 8k atau 64k byte.

bashbaca skrip baris demi baris. Namun, mengingat garis fakta bisa panjang sewenang-wenang, itu membaca setiap kali 8176 byte dari awal baris berikutnya menjadi parse.

Ini untuk konstruksi sederhana, yaitu serangkaian perintah sederhana.

Jika perintah terstruktur shell digunakan ( kasus jawaban yang diterima tidak dapat dipertimbangkan ) seperti for/do/doneloop, case/esacswitch, dokumen di sini, subkulit yang terlampir oleh tanda kurung, definisi fungsi, dll. Dan kombinasi apa pun di atas, penerjemah shell membaca ke akhir konstruksi untuk memastikan terlebih dahulu tidak ada kesalahan sintaksis.

Ini agak tidak efisien karena kode yang sama dapat dibaca berulang kali dalam jumlah besar tetapi dimitigasi oleh fakta bahwa konten ini biasanya di-cache.

Apa pun penerjemah shell, sangat tidak bijaksana untuk memodifikasi skrip shell ketika sedang dieksekusi karena shell bebas untuk membaca lagi setiap bagian skrip dan ini dapat menyebabkan kesalahan sintaksis yang tidak terduga jika tidak sinkron.

Perhatikan juga bahwa bash mungkin macet dengan pelanggaran segmentasi ketika tidak dapat menyimpan konstruksi skrip yang terlalu besar yang bisa dibaca oleh ksh93 dengan sempurna.

Jlliagre
sumber
7

Itu tergantung pada bagaimana penerjemah yang menjalankan skrip bekerja. Semua yang dilakukan kernel adalah memperhatikan file yang akan dijalankan dimulai #!, pada dasarnya menjalankan sisa baris sebagai sebuah program dan memberikannya file executable sebagai argumen. Jika penerjemah yang terdaftar di sana membaca file itu baris demi baris (seperti shell interaktif lakukan dengan apa yang Anda ketik), itulah yang Anda dapatkan (tetapi struktur multi-line loop dibaca dan disimpan untuk pengulangan); jika penerjemah mencabut file ke dalam memori, memprosesnya (mungkin mengkompilasinya ke representasi perantara, seperti Perl dan Pyton lakukan) file tersebut dibaca secara penuh sebelum dieksekusi.

Jika Anda menghapus file sementara itu, file tersebut tidak dihapus sampai juru bahasa menutupnya (seperti biasa, file hilang ketika referensi terakhir, baik itu entri direktori atau proses menjaganya tetap terbuka) menghilang.

vonbrand
sumber
4

File 'x':

cat<<'dog' >xyzzy
LANG=C
T=`tty`
( sleep 2 ; ls -l xyzzy >$T ) &
( sleep 4 ; rm -v xyzzy >$T ) &
( sleep 4 ; ls -l xyzzy >$T ) &
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
dog

sh xyzzy

Lari:

~/wrk/tmp$ sh x
alive.
alive.
alive.
-rw-r--r-- 1 yeti yeti 287 Mar 23 16:57 xyzzy
alive.
removed `xyzzy'
ls: cannot access xyzzy: No such file or directory
alive.
alive.
alive.
alive.
~/wrk/tmp$ _

IIRC file tidak dihapus selama proses tetap terbuka. Penghapusan hanya menghapus DIRENT yang diberikan.


sumber