2008-10-24 115 views
12

Python對環境變量的訪問不能準確反映操作系統對進程環境的看法。Linux上的Python環境變量

os.getenv和os.environ在特定情況下無法正常工作。

有沒有辦法正確獲取正在運行的進程環境?


爲了證明我的意思,拿兩個大致相當於程序(先在C,另一個在python):

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
int main(int argc, char *argv[]){ 
    char *env; 
    for(;;){ 
     env = getenv("SOME_VARIABLE"); 
     if(env) 
      puts(env); 
     sleep(5); 
    } 
} 

import os 
import time 
while True: 
    env = os.getenv("SOME_VARIABLE") 
    if env is not None: 
     print env 
    time.sleep(5) 

現在,如果我們運行C程序並使用gdb附加到正在運行的進程,並通過執行如下操作來強制更改引擎蓋下的環境:

(gdb) print setenv("SOME_VARIABLE", "my value", 1) 
[Switching to Thread -1208600896 (LWP 16163)] 
$1 = 0 
(gdb) print (char *)getenv("SOME_VARIABLE") 
$2 = 0x8293126 "my value" 

那麼上述的C程序將開始每5秒噴出一次「我的價值」一次。然而,上述的python程序不會。

有沒有辦法讓python程序在這種情況下像C程序一樣運行?

(是的,我知道這是對正在運行的進程進行一個非常模糊的和潛在的破壞作用)

而且,我目前使用python 2.4,這可能被固定在蟒蛇的更高版本。

+0

對於它的價值,這並不令人意外:爲os模塊庫參考突出的問題。 – bobince 2008-10-24 22:35:50

回答

14

這是一個非常好的問題。

事實證明,所述os模塊初始化os.environ到的posix.environ的值,其被設定在解釋器啓動。換句話說,標準庫似乎不提供對getenv函數的訪問。

這是一個在unix上使用ctypes可能是安全的情況。既然你會叫一個超標準的libc函數。

1

望着Python源代碼(2.4.5):

  • 模塊/ posixmodule.c獲取convertenviron的ENVIRON(),它被啓動時運行(見INITFUNC),並在存儲環境相關平臺模塊(NT,OS2或POSIX)

  • 庫/ os.py看着sys.builtin_module_names,進口全部來自任何POSIX符號,NT或OS2

所以是的,它在啓動時得到決定。 os.environ在這裏不會有幫助。

如果你真的想這樣做,那麼想到的最明顯的方法是創建自己的基於C的自定義python模塊,其中getenv總是調用系統調用。

+0

或者我可以使用ctypes模塊,但現在只是破壞了它的樂趣,不是嗎? – Sufian 2008-10-24 22:18:06

3

我不相信有很多的程序也希望有自己的環境外部修改,在啓動時加載所以傳遞環境的副本等同。你只是偶然發現了一個實現選擇。

如果從內部程序的工作原理看到所有的設置在-啓動值和運行putenv/SETENV,我不認爲有什麼可擔心。有更簡潔的方法將更新的信息傳遞給正在運行的可執行文件。

4

另一種可能性是使用PDB,或一些其它蟒調試器代替,並且在蟒級別更改os.environ,而不是C水平。 Here's我發佈的一個小配方,用於中斷正在運行的python進程,並在接收信號時提供對python控制檯的訪問。或者,只需在您想要中斷的代碼中的某處插入pdb.set_trace()即可。在任何一種情況下,只需運行語句「import os; os.environ['SOME_VARIABLE']='my_value'」,就python而言應該更新。

我不知道這是否也將更新C環境SETENV,因此,如果您使用的getenv擁有的C模塊直接您可能需要做更多的工作來保持這種同步。

10

您可以使用​​做到這一點很簡單:

>>> from ctypes import CDLL, c_char_p 
>>> getenv = CDLL("libc.so.6").getenv 
>>> getenv.restype = c_char_p 
>>> getenv("HOME") 
'/home/glyph'