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

5 投票
2 回答
6695 浏览
提问于 2025-04-18 06:56

我正在写一个构建脚本,用来解决依赖的共享库(还有它们的共享库等等)。这些共享库不在正常的 PATH 环境变量里。

为了让构建过程顺利进行(让编译器能找到这些库),我修改了 PATH,把这些库的目录加上去了。

构建过程是这样的:

加载脚本(修改 PATH) -> 基于Python的构建脚本 -> 配置 -> 构建 -> 解决依赖 -> 安装。

这个Python实例从它的父shell那里继承了修改过的 PATH 变量。

在Python里面,我想获取默认的 PATH(不是从父shell继承来的那个)。

我的想法:

获取“默认” 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 个回答

0

你说的“PATH的默认值”到底是什么意思呢?是你登录时的值吗?还是某种系统范围内的默认值?又或者是加载脚本开始时的值,在它做出更改之前的值?

最简单的方法就是,如果你真的不能修改加载脚本,可以用你自己的脚本把当前的PATH值保存到另一个环境变量,比如叫OLD_PATH。然后你可以用类似这样的代码:

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

或者你可以启动一个登录的shell,或者至少是一个交互式的shell,让它先加载用户的.bashrc(或者其他启动文件),然后再执行python。

** 更新 ** 如果是在Windows上,并且你只是想获取PATH值的话:

启动CMD.EXE,执行命令'echo %PATH%'。

6

使用 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,它必须是一个映射,定义了新进程的环境变量;这些变量会被用来替代当前进程的环境变量,而后者是默认的行为。

撰写回答