Mengapa saya tidak bisa mencetak variabel yang bisa saya lihat di output env?

9

Saya tertarik untuk mengatur variabel lingkungan dari satu contoh shell dari yang lain. Jadi saya memutuskan untuk melakukan riset. Setelah membaca sejumlah dari pertanyaan tentang ini saya memutuskan untuk menguji itu.

Saya menelurkan dua cangkang A dan B (PID 420), keduanya berjalan zsh. Dari shell AI berlari berikut ini.

sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach

Dari shell B ketika saya menjalankan envsaya dapat melihat variabel FOO memang diatur dengan nilai bar. Ini membuat saya berpikir bahwa FOO telah berhasil diinisialisasi di lingkungan shell B. Namun, jika saya mencoba untuk mencetak FOO saya mendapatkan garis kosong yang menyiratkan bahwa itu tidak disetel. Bagi saya, rasanya ada kontradiksi di sini.

Ini diuji pada Arch GNU / Linux system saya sendiri dan Ubuntu VM. Saya juga menguji ini di bashmana variabel bahkan tidak muncul di env. Ini walaupun mengecewakan bagi saya, masuk akal jika shell menyimpan salinan lingkungannya pada waktu spawn dan hanya menggunakannya (yang disarankan dalam salah satu pertanyaan terkait). Ini masih tidak menjawab mengapa zshbisa melihat variabel.

Mengapa output echo $FOOkosong?


EDIT

Setelah masukan dalam komentar saya memutuskan untuk melakukan pengujian sedikit lebih. Hasilnya dapat dilihat pada tabel di bawah ini. Di kolom pertama adalah shell yang FOOvariabelnya diinjeksikan. Baris pertama berisi perintah yang outputnya bisa dilihat di bawahnya. Variabel FOOdisuntikkan menggunakan: sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'. Perintah khusus untuk zsh: zsh -c '...'juga diuji menggunakan bash. Hasilnya identik, hasilnya dihilangkan untuk singkatnya.

Arch GNU / Linux, zsh 5.3.1, bash 4.4.12 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ubuntu 16.04.2 LTS, zsh 5.1.1, bash 4.3.48 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Di atas tampaknya menyiratkan bahwa hasilnya agnostik distribusi. Ini tidak memberi tahu saya lebih dari zshdan bashmenangani pengaturan variabel secara berbeda. Lebih jauh lagi, export FOOmemiliki perilaku yang sangat berbeda dalam konteks ini tergantung pada shell. Semoga tes ini dapat membuat sesuatu menjadi jelas bagi orang lain.

rlf
sumber
Apa yang terjadi, jika Anda melakukan zsh -c 'echo $FOO'(gunakan tanda kutip tunggal!)? Bisakah Anda melihatnya?
user1934428
Nilai yang benar dicetak dari sub shell baru (juga diuji untuk anak bash). Jelas lingkungan itu persisten entah bagaimana karena anak dapat mewarisinya tetapi mengapa orang tua tidak menghargainya?
rlf
3
Itulah yang saya pikir. Saya kira shell memiliki suatu tempat simbol tabel variabel, beberapa dari mereka ditandai sebagai "diekspor", yang berarti bahwa ketika membuka subkulit, mereka ditempatkan ke dalam lingkungan proses anak. Awalnya (ketika shell dimulai), variabel-variabel dari lingkungan pada waktu itu disalin ke dalam tabel simbol (tentu saja juga sebagai variabel "yang diekspor"). Ketika Anda mengubah lingkungan, shell tidak diketahui untuk memperbarui tabel simbol mereka - tetapi proses anak (seperti env) melihat lingkungan yang dimodifikasi.
user1934428
2
Saya menguji pada Ubuntu 16.04 dengan zsh 5.1.1 dan bash 4.3.48 (1) dan tampaknya mengatur variabel lingkungan untuk zshdi GDB tidak membuatnya terlihat sebagai variabel shell tetapi tidak menyebabkannya diteruskan ke proses anak (seperti Anda telah mengamati), sedangkan pengaturan satu untuk bash tidak membuatnya terlihat sebagai variabel shell tapi tidak tidak menyebabkan itu untuk diteruskan ke proses anak! Sepertinya zsh dan bash menggunakan berbagai strategi untuk mengelola variabel, dengan zsh melacak variabel non-lingkungan dan bash menyimpan segala sesuatu di lingkungannya yang disanitasi ketika meluncurkan anak (non-kulit).
Eliah Kagan
@EliahKagan, menarik; Anda harus memposting itu sebagai jawaban. Saya juga ingin tahu apakah itu membuat perbedaan jika Anda menjalankan export FOOdi bash?
Wildcard

Jawaban:

2

Kebanyakan kerang tidak menggunakan getenv()/ setenv()/ putenv()API.

Saat start-up, mereka membuat variabel shell untuk setiap variabel lingkungan. Itu akan disimpan dalam struktur internal yang perlu membawa informasi lain seperti apakah variabel diekspor, hanya-baca ... Mereka tidak dapat menggunakan libc environuntuk itu.

Demikian pula, dan untuk alasan itu, mereka tidak akan menggunakan execlp(), execvp()untuk mengeksekusi perintah tetapi memanggil panggilan execve()sistem secara langsung, menghitung envp[]array berdasarkan daftar variabel yang diekspor.

Jadi di Anda gdb, Anda perlu menambahkan entri ke tabel internal variabel shell, atau mungkin memanggil fungsi yang tepat yang akan membuatnya menafsirkan export VAR=valuekode untuk memperbaruinya sendiri.

Mengapa Anda melihat perbedaan antara bashdan zshketika Anda menelepon setenv()di gdb, saya menduga bahwa karena Anda menelepon setenv()sebelum inisialisasi shell, misalnya saat memasuki main().

Anda akan melihat bash's main()adalah int main(int argc, char* argv[], char* envp[])(dan bashpeta variabel dari env yang vars di envp[]) sementara zsh' s adalah int main(int argc, char* argv[])dan zshmendapat variabel dari environsebaliknya. setenv()tidak memodifikasi environtetapi tidak dapat memodifikasi envp[]di tempat (baca-saja pada beberapa sistem serta string yang ditunjukkan oleh pointer).

Dalam kasus apa pun, setelah shell membaca environsaat startup, menggunakan setenv()tidak akan efektif karena shell tidak lagi menggunakan environ(atau getenv()) sesudahnya.

Stéphane Chazelas
sumber