嗯,似乎环境变量的情况在python中不一致。
使用os.environ
或os.getenv
读取环境变量返回导入os
模块时的env状态并不是秘密。仍然可以使用分配到os.environ
密钥来更新环境。
但是一旦我使用os.putenv
或运行任何修改了环境的ctypes代码,我就会在实际的流程环境和os.environ
之间产生不一致。 Nuff说,无论是用os.system
还是subprocess
库创建的,都会为子进程保留这个实际环境。在我看来,这是他们想要的行为。
现在我想查看并更改传递给子进程的环境。通常建议您获取os.environ
的副本,修改它并作为参数传递给subprocess.Popen
。但在这种情况下,由ctypes代码对环境所做的更新将会丢失。
有没有办法克服这个问题?严格来说有没有办法重新加载os.environ或使用其他工具获取实际环境的副本?
答案 0 :(得分:3)
os.putenv()
不会将os.environ
更新为its docs say explicitly。 C putenv()
(在CPython扩展模块中)也不会更新os.environ
(如文档所述:os
导入后环境中的更改未反映在os.environ
中)。
os.getenv(var)
is just os.environ.get(var)
。 related Python issue为@ShadowRanger has mentioned。
如果你需要它;您可以使用ctypes
从Python访问C environ,例如(在Ubuntu上测试,它可能适用于OS X(您可能需要调用_NSGetEnviron()
there),它不太适用于Windows(use _wenviron
there)):
import ctypes
libc = ctypes.CDLL(None)
environ = ctypes.POINTER(ctypes.c_char_p).in_dll(libc, 'environ')
environ
是指向C(NUL终止)字符串(char*
)数组的指针,其中最后一项是NULL
。要枚举Python 2中的值:
for envvar in iter(iter(environ).next, None):
print envvar
LC_PAPER=en_GB.UTF-8
LC_ADDRESS=en_GB.UTF-8
CLUTTER_IM_MODULE=xim
LC_MONETARY=en_GB.UTF-8
VIRTUALENVWRAPPER_PROJECT_FILENAME=.project
SESSION=ubuntu
...
要将其作为可以修改并传递给子进程的字典:
env = dict(envvar.split(b'=', 1) for envvar in iter(iter(environ).next, None))
与os.environ
同步:
os.environ.clear() # NOTE: it clears C environ too!
getattr(os, 'environb', os.environ).update(env) # single source Python 2/3 compat.
以下是几个便利功能:
#!/usr/bin/env python
import ctypes
import functools
import os
_environ = None
def get_libc_environb_items():
"""Get envvars from C environ as bytestrings (unsplit on b'=')."""
global _environ
if _environ is None:
libc = ctypes.CDLL(None)
_environ = ctypes.POINTER(ctypes.c_char_p).in_dll(libc, 'environ')
return iter(functools.partial(next, iter(_environ)), None)
def get_libc_environb():
"""Get a copy of C environ as a key,value mapping of bytestrings."""
return dict(k_v.split(b'=', 1) for k_v in get_libc_environb_items()
if b'=' in k_v) # like CPython
def get_libc_environ():
"""Get a copy of C environ as a key,value mapping of strings."""
environb = get_libc_environb()
# XXX sys.getfilesystemencoding()+'surrogateescape'
fsdecode = getattr(os, 'fsdecode', None)
if fsdecode is None: # Python 2
return environb # keep bytestrings as is (`str` type)
else: # Python 3, decode to Unicode
return {fsdecode(k): fsdecode(v) for k, v in environb.items()}
def synchronize_environ():
"""Synchronize os.environ with C environ."""
libc_environ = get_libc_environ()
os.environ.clear()
os.environ.update(libc_environ)
def test():
assert 'SPAM' not in os.environ
assert 'SPAM' not in get_libc_environ()
os.putenv('SPAM', 'egg')
assert 'SPAM' not in os.environ
assert os.getenv('SPAM') is None
assert get_libc_environ()['SPAM'] == 'egg'
assert os.popen('echo $SPAM').read().strip() == 'egg'
synchronize_environ()
assert os.environ['SPAM'] == 'egg'
if __name__ == "__main__":
test()
from pprint import pprint
pprint(get_libc_environ())
适用于CPython 2,CPython 3,pypy。它不适用于Jython。
答案 1 :(得分:2)
这是StructType
。 os.getenv
从os.environ
读取,并在os.environ
上设置项隐式执行os.putenv
,删除隐式调用os.unsetenv
等。
但即使os.getenv
从os.environ
读取,os.putenv
也不会写入(known issue with Python, as yet unfixed)。并且this behavior is documented。基本上,如果您想要一致的环境,则必须仅更新os.environ
,而不是使用os.putenv
;如果ctypes
来电直接更新C级environ
,则您需要进行另一次ctypes
调用才能阅读C级environ
并更新os.environ
匹配。