使用“默认”环境变量启动新的子进程

4
我正在编写一个构建脚本,用于解决依赖的共享库(以及它们的共享库等)。这些共享库不存在于正常的PATH环境变量中。
为了使构建过程正常工作(让编译器找到这些库),已更改PATH以包括这些库的目录。
构建过程如下:
加载器脚本(更改PATH)->基于Python的构建脚本->配置->构建->解决依赖关系->安装。
Python实例从其父Shell继承了更改后的PATH变量。
在Python内部,我试图获取默认的PATH(而不是从其父Shell继承的PATH)。
想法:
解决“默认”PATH变量的想法是通过某种方式“信号”操作系统启动一个新进程(运行打印PATH的脚本),但此进程不是当前Python进程的子进程(并且可能不会继承其修改的环境变量)。
尝试的实现:
import os
import sys

print os.environ["PATH"]
print "---"
os.spawnl(os.P_WAIT, sys.executable, "python", "-c \"import os;print(os.environ['PATH']);\"")

os.spawn 看起来使用与调用它的 Python 进程相同的环境变量。我也尝试过使用 subprocess.POpen,但没有成功。

这种方法可行吗?如果不行,有什么替代方案(考虑到加载器脚本和整个进程不能改变)?

我目前正在使用 Windows,但构建脚本应该跨平台。

编辑:

跨平台的限制似乎太严格了。现在可以考虑相同概念的不同实现。

例如,使用答案中的代码,可以使用 Windows 注册表获取“默认”的系统 PATH 变量。

try:
    import _winreg as winreg
except ImportError:
    try:
        import winreg
    except ImportError:
        winreg = None

def env_keys(user=True):
    if user:
        root = winreg.HKEY_CURRENT_USER
        subkey = "Environment"
    else:
        root = winreg.HKEY_LOCAL_MACHINE
        subkey = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
    return root, subkey

def get_env(name, user=True):
    root, subkey = env_keys(user)
    key = winreg.OpenKey(root, subkey, 0, winreg.KEY_READ)
    try:
        value, _ = winreg.QueryValueEx(key, name)
    except WindowsError:
        return ""
    value = winreg.ExpandEnvironmentStrings(value)
    return value

print get_env("PATH", False)

需要一个在*nix系统下一致的方法。
2个回答

5

使用subprocess.Popen,您可以为子进程提供一个环境:

default_path = os.environ['PATH'] # save the default path before changing it
os.environ['PATH'] = # whatever you want
child_env = os.environ.copy()
child_env['PATH'] = default_path
# change env
subprocess.Popen(..., env=child_env)

根据文档,提供的环境将被用于替代从父进程继承的环境:

如果env不是None,则必须是一个映射,定义新进程的环境变量;这些变量将被用来替代继承当前进程的环境,这是默认行为。


Python的构建脚本不能更改PATH变量,因为还有其他非Python工具需要修改PATH。因此,PATH的更改必须在加载脚本中进行。 - dilbert
我编辑了我的答案以反映我认为你试图做什么,但我真的不确定我完全理解这个问题。 - dano
啊等等,加载器脚本不是用Python写的吗?你能不能在调用Python脚本之前让加载器脚本保存默认路径到一个环境变量中呢? - dano
对于任何混淆,我表示歉意。加载器脚本是一个 .bat 文件,必须保持不变。我已经编辑了问题。你知道一种方法可以“信号”操作系统启动一个不是 Python 子进程的新进程吗? - dilbert
Python脚本应该是可移植的,但装载程序是特定于平台的。我不想在构建脚本中放置任何对备份路径的引用。 - dilbert

0

默认值'PATH'的真正含义是什么?它是您登录时的值吗?还是某个系统范围内的默认值?或者是加载脚本在进行更改之前的起始值?

最简单的方法是使用自己的包装器脚本(如果您真的不能更改它),将'PATH'当前值保存到另一个环境变量中,比如'OLD_PATH'。然后您可以使用类似以下的内容:

os.spawnle( ... , {'PATH' : os.environ['OLD_PATH']})

或者您可以生成一个作为登录或至少交互式shell的shell,并让它在调用python之前获取用户的.bashrc(或其他启动文件)。
** 更新 ** 适用于Windows,假设您只想获取PATH:
生成CMD.EXE,让它执行命令'echo %PATH%'

在这种情况下,默认值被认为是加载器脚本之前的环境。我不想将环境变量传递给子进程;我正在寻找子进程返回默认环境。尽管构建脚本是跨平台的,但这是在Windows上进行的。 - dilbert
我看到您想让生成的进程告诉您它的环境。那么,我的答案的第二部分就是:生成一个正在运行CMD.EXE(Windows)或bash(Linux)的进程,并让它告诉您它的PATH。为这些细节更新答案。 - BobHy
我考虑过这个问题。有没有一种方法可以“向操作系统发信号”,以启动一个新进程(但不作为当前Python进程的子进程)? - dilbert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接