Mengapa variabel terlihat dalam subkulit?

18

Learning Bash Book menyebutkan bahwa subkulit hanya akan mewarisi variabel lingkungan dan deskriptor file, dll., Dan itu tidak akan mewarisi variabel yang tidak diekspor:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

Seperti yang saya tahu shell akan membuat dua subkulit untuk ()dan untuk ./file, tetapi mengapa dalam ()kasus ini subkulit mengidentifikasi varvariabel meskipun tidak diekspor dan dalam ./filekasus itu tidak mengidentifikasi itu?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

Saya mencoba menggunakan straceuntuk mencari tahu bagaimana ini terjadi dan mengejutkan saya menemukan bahwa bash akan menggunakan argumen yang sama untuk panggilan sistem klon, jadi ini berarti bahwa kedua proses bercabang di ()dan ./fileharus memiliki ruang alamat proses proses yang sama dari induk, jadi mengapa dalam ()kasus ini apakah variabel terlihat oleh subkulit dan hal yang sama tidak terjadi untuk ./filekasus, meskipun argumen yang sama didasarkan pada panggilan sistem klon?

pengguna3718463
sumber
vinc17 mengatakan benar, bahkan Anda mendapatkan pstree ketika Anda memiliki subkulit, Anda percaya hal ini.
PersianGulf

Jawaban:

15

Buku Bash Belajar salah. Subshell mewarisi semua variabel. Bahkan $$(PID dari shell asli) disimpan. Alasannya adalah bahwa untuk subkulit, shell hanya bercabang dan tidak mengeksekusi shell baru (sebaliknya, ketika Anda mengetik ./file, perintah baru dieksekusi, misalnya shell baru; pada output strace, lihat pada execvedan mirip) . Jadi, pada dasarnya, itu hanya salinan (dengan beberapa perbedaan yang terdokumentasi).

Catatan: ini tidak khusus untuk bash; ini berlaku untuk shell apa pun.

vinc17
sumber
Oky tetapi saya mencoba sekarang untuk meluncurkan strace pada shell dan saya mencoba untuk mengeksekusi ./file tetapi saya tidak dapat menemukan panggilan untuk exec dan jadi ruang alamat harus sama untuk kedua proses jadi bagaimana ini bisa dijelaskan?
user3718463
@ user3718463 Apakah Anda menggunakan -fopsi straceuntuk melacak anak-anak juga? Itu perlu untuk menemukan eksekutif itu.
vinc17
ya saya mengatasinya terima kasih banyak, saya kehilangan opsi -f, dan jadi saya tidak dapat menemukan panggilan sys exec
user3718463
16

Entah Anda atau buku ini membingungkan subkulit dengan subproses yang merupakan shell.

Beberapa konstruksi shell menghasilkan shell forking proses anak. Di Linux, forkadalah kasus khusus dari clonepemanggilan sistem yang lebih umum , yang Anda amati dalam stracelog. Anak itu menjalankan bagian dari skrip shell. Proses anak disebut subkulit . Konstruk semacam itu yang paling langsung adalah command1 &: command1dijalankan dalam subkulit, dan perintah selanjutnya dijalankan dalam shell induk. Konstruksi lain yang membuat subkulit meliputi substitusi perintah $(command2)dan pipa command3 | command4( command3berjalan dalam subkulit, command4berjalan dalam subkulit di sebagian besar shell tetapi tidak dalam ksh atau zsh).

Subkulit adalah salinan dari proses induk, sehingga tidak hanya memiliki variabel lingkungan yang sama, tetapi juga semua definisi internal yang sama: variabel (termasuk $$, ID proses dari proses shell asli), fungsi, alias, opsi, dll. Sebelum mengeksekusi kode dalam subkulit, bash menetapkan variabel BASHPIDke ID proses proses anak.

Ketika Anda menjalankan ./file, ini menjalankan perintah eksternal. Pertama, shell membuat proses anak; maka proses anak ini mengeksekusi (dengan execvepanggilan sistem) file yang dapat dieksekusi ./file. Proses anak mewarisi atribut proses dari orang tuanya: lingkungan, direktori saat ini, dll. Aspek internal aplikasi hilang dalam execvepanggilan: variabel yang tidak diekspor, fungsi, dll. Adalah gagasan bash yang tidak diketahui oleh kernel, dan mereka hilang ketika bash menjalankan program lain. Bahkan jika program lain itu adalah skrip bash, ia dijalankan oleh instance baru dari bash yang tidak tahu atau peduli bahwa proses induknya juga merupakan instance dari bash. Dengan demikian variabel shell (variabel yang tidak diekspor) tidak bertahan execve.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Jawaban ini menjelaskan beberapa hal bagi saya. Satu-satunya hal yang saya tidak mengerti adalah kalimat ini di paragraf kedua: "Anak itu menjalankan bagian dari skrip shell." Script shell apa yang dirujuk?
flow2k
@ flow2k Skrip (yaitu program) yang ditafsirkan oleh shell.
Gilles 'SO- stop being evil'