Linux

為什麼我不能列印在 env 的輸出中可以看到的變數?

  • June 6, 2020

我有興趣從另一個 shell 實例設置環境變數。所以我決定做一些研究。在閱讀了一些關於問題後,我決定對其進行測試。

我生成了兩個 shell A 和 B (PID 420),都在執行zsh. 從 shell AI 執行以下內容。

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

從 shell B 執行時,env我可以看到變數 FOO 確實設置了值 bar。這讓我認為 FOO 已經在 shell B 的環境中成功初始化。但是,如果我嘗試列印 FOO,我會得到一個空行,表示它沒有設置。對我來說,感覺這裡有一個矛盾。

這在我自己的 Arch GNU/Linux 系統和 Ubuntu VM 上都進行了測試。我還測試了這個bash變數甚至沒有出現在環境中的地方。雖然這對我來說令人失望,但如果 shell 在生成時記憶體其環境的副本並且只使用它(這是在連結問題之一中提出的),這是有道理的。這仍然不能回答為什麼zsh可以看到變數。

為什麼輸出是echo $FOO空的?


編輯

在評論中輸入之後,我決定做更多的測試。結果可見下表。第一列是FOO變數被注入的外殼。第一行包含可以在其下方看到其輸出的命令。變數FOO是使用注入的:sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'。特定於 zsh: 的命令zsh -c '...'也使用 bash 進行了測試。結果是相同的,為簡潔起見省略了它們的輸出。

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 |

以上似乎暗示結果與分佈無關。這並沒有告訴我更多,zsh並且bash以不同的方式處理變數的設置。此外,export FOO根據外殼,在這種情況下具有非常不同的行為。希望這些測試可以讓其他人明白一些事情。

大多數 shell 不使用getenv()// API setenv()putenv()

在啟動時,他們為收到的每個環境變數創建 shell 變數。這些將儲存在需要攜帶其他資訊的內部結構中,例如變數是否已導出,只讀……他們不能environ為此使用libc。

同樣,出於這個原因,他們不會使用execlp(),execvp()來執行命令,而是execve()直接呼叫系統呼叫,envp[]根據導出的變數列表計算數組。

因此,在您的 中gdb,您需要向該 shell 內部變數表添加一個條目,或者可能呼叫正確的函式,使其解釋export VAR=value程式碼以自行更新該表。

至於為什麼您會在呼叫inbash和呼叫時看到差異,我懷疑這是因為您在 shell 初始化之前呼叫,例如在輸入zsh``setenv()``gdb``setenv()``main()

您會注意到bash’s main()is int main(int argc, char* argv[], char* envp[])(並bash映射來自 env vars 中的變數envp[]),而zsh’s isint main(int argc, char* argv[])zsh從中獲取變數environsetenv()確實修改environ但不能envp[]就地修改(在幾個系統上以及這些指針指向的字元串上是只讀的)。

在任何情況下,在 shellenviron在啟動時讀取之後, usingsetenv()將無效,因為 shell 之後不再使用environ(或getenv())。

引用自:https://unix.stackexchange.com/questions/377300