red*_*dot 6 python subprocess environment-variables
好吧,似乎环境变量的情况在python中不一致。
在导入模块时使用os.environ或os.getenv返回 env 的状态来读取环境变量并不是什么秘密os。仍然可以使用分配给os.environ键来更新环境。
但是一旦我使用os.putenv或运行任何修改环境的 ctypes 代码,我就会发现实际进程环境和os.environ. Nuff 说,这个实际的环境将被保留给子进程,无论是用创建的os.system还是subprocess库的。就我而言,这是理想的行为。
现在我想查看和更改传递给子流程的环境。通常建议获取 的副本os.environ,修改它并作为参数传递以进行subprocess.Popen调用。但在这种情况下,由 ctypes 代码对环境所做的更新将丢失。
有没有办法克服这个问题?严格来说,有没有办法重新加载 os.environ 或使用其他工具获取具有实际环境的副本?
os.putenv()不会os.environ按照其文档明确说明进行更新。C putenv()(在 CPython 扩展模块中)也不会更新os.environ(如文档所述:os导入后环境中的更改未反映在 中os.environ)。
os.getenv(var)只是os.environ.get(var). 正如@ShadowRanger 提到的那样,存在相关的 Python 问题。
如果你需要它;您可以使用例如从 Python访问C 环境ctypes(在 Ubuntu 上测试,它可能在 OS X 上工作(您可能需要在_NSGetEnviron()那里调用),它不太可能在 Windows 上工作(在那里使用_wenviron)):
import ctypes
libc = ctypes.CDLL(None)
environ = ctypes.POINTER(ctypes.c_char_p).in_dll(libc, 'environ')
Run Code Online (Sandbox Code Playgroud)
environ是指向 C(以 NUL 结尾的)字符串 ( char*)数组的指针,其中最后一项是NULL。要枚举 Python 2 中的值:
for envvar in iter(iter(environ).next, None):
print envvar
Run Code Online (Sandbox Code Playgroud)
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
...
Run Code Online (Sandbox Code Playgroud)
要将其作为可以修改并传递给子进程的字典:
env = dict(envvar.split(b'=', 1) for envvar in iter(iter(environ).next, None))
Run Code Online (Sandbox Code Playgroud)
与 同步os.environ:
os.environ.clear() # NOTE: it clears C environ too!
getattr(os, 'environb', os.environ).update(env) # single source Python 2/3 compat.
Run Code Online (Sandbox Code Playgroud)
这里有几个方便的功能:
#!/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())
Run Code Online (Sandbox Code Playgroud)
它适用于 CPython 2、CPython 3、pypy。它不适用于 Jython。