python:获取实际环境变量以修改并传递给子进程
python: getting actual environment variables to modify and pass to subprocess
嗯,环境变量的情况似乎与 python 不一致。
使用 os.environ
或 os.getenv
returns 导入 os
模块时 env 的状态读取环境变量已经不是什么秘密了。仍然可以使用分配给 os.environ
键来更新环境。
但是一旦我使用 os.putenv
或 运行 任何修改了环境的 ctypes 代码,我就会发现实际进程环境与 os.environ
之间存在不一致。 Nuff 说,无论使用 os.system
还是 subprocess
库创建,这个实际环境都会为子进程保留。就我而言,这是理想的行为。
现在我想查看和更改传递给子流程的环境。通常建议获取 os.environ
的副本,修改它并作为参数传递给 subprocess.Popen
调用。但在这种情况下,由 ctypes 代码对环境所做的更新将会丢失。
有什么办法可以解决这个问题吗?严格来说,有没有办法重新加载 os.environ 或使用其他设施获得具有实际环境的副本?
这是一个known issue with Python, as yet unfixed。 os.getenv
从 os.environ
读取,并在 os.environ
上设置项目隐式执行 os.putenv
,删除隐式调用 os.unsetenv
,等等
但是即使 os.getenv
从 os.environ
读取,os.putenv
也不会写入(和 this behavior is documented). And there doesn't appear to be a way to make it reread the os.environ
)。基本上,如果你想要一个一致的环境,你必须只更新 os.environ
,而不是使用 os.putenv
;如果 ctypes
调用直接更新 C 级别 environ
,您将需要另一个 ctypes
调用读取 C 级别 environ
并更新 os.environ
以匹配。
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)
. There is related Python issue as .
如果您需要;您可以访问 C environ from Python using ctypes
e.g. (tested on Ubuntu, it might work on OS X (you might need to call _NSGetEnviron()
there), it is unlikely to work on 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。
嗯,环境变量的情况似乎与 python 不一致。
使用 os.environ
或 os.getenv
returns 导入 os
模块时 env 的状态读取环境变量已经不是什么秘密了。仍然可以使用分配给 os.environ
键来更新环境。
但是一旦我使用 os.putenv
或 运行 任何修改了环境的 ctypes 代码,我就会发现实际进程环境与 os.environ
之间存在不一致。 Nuff 说,无论使用 os.system
还是 subprocess
库创建,这个实际环境都会为子进程保留。就我而言,这是理想的行为。
现在我想查看和更改传递给子流程的环境。通常建议获取 os.environ
的副本,修改它并作为参数传递给 subprocess.Popen
调用。但在这种情况下,由 ctypes 代码对环境所做的更新将会丢失。
有什么办法可以解决这个问题吗?严格来说,有没有办法重新加载 os.environ 或使用其他设施获得具有实际环境的副本?
这是一个known issue with Python, as yet unfixed。 os.getenv
从 os.environ
读取,并在 os.environ
上设置项目隐式执行 os.putenv
,删除隐式调用 os.unsetenv
,等等
但是即使 os.getenv
从 os.environ
读取,os.putenv
也不会写入(和 this behavior is documented). And there doesn't appear to be a way to make it reread the os.environ
)。基本上,如果你想要一个一致的环境,你必须只更新 os.environ
,而不是使用 os.putenv
;如果 ctypes
调用直接更新 C 级别 environ
,您将需要另一个 ctypes
调用读取 C 级别 environ
并更新 os.environ
以匹配。
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)
. There is related Python issue as
如果您需要;您可以访问 C environ from Python using ctypes
e.g. (tested on Ubuntu, it might work on OS X (you might need to call _NSGetEnviron()
there), it is unlikely to work on 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。