更改当前进程环境的LD_LIBRARY_PATH

26

是否可以更改当前进程的环境变量?

更具体地说,在Python脚本中,我想要更改LD_LIBRARY_PATH,以便在导入依赖于某些xyz.so的模块“x”时,xyz.so从我的给定路径的LD_LIBRARY_PATH中获取。

是否有其他动态更改库加载路径的方式?

编辑:我认为我需要说明的是,我已经尝试过如下操作:

os.environ["LD_LIBRARY_PATH"] = mypath
os.putenv('LD_LIBRARY_PATH',mypath)

但这些修改了生成的子进程的环境变量,而不是当前进程,模块加载也不考虑新的LD_LIBRARY_PATH。

编辑2: 所以问题是:我们是否可以更改环境或其他内容,以使库加载器看到它并从那里加载?


这不是 https://dev59.com/YXRA5IYBdhLWcg3wtwSe 的重复吗?你实际上并不是在问如何改变环境,而是如何改变 Python 加载库的位置。 - Bryan Oakley
好的!我会说这是重复的,但它不是关于 Python 的,而是关于改变环境,例如在一个加载动态库 A 的 C 应用程序中,我们是否可以更改环境,以便从我们选择的路径加载 A。 - Anurag Uniyal
5个回答

39
理由
os.environ["LD_LIBRARY_PATH"] = ...
不起作用很简单:这个环境变量控制动态加载器的行为(在Linux上是 ld-linux.so.2,在Solaris上是 ld.so.1),但是加载器仅在进程启动时查看一次LD_LIBRARY_PATH。在那之后更改LD_LIBRARY_PATH的值将不会产生任何效果(就像这个问题的回答所说的那样)。
你有一些选项:
A. 如果你知道你需要从 /some/path 获取 xyz.so ,并且从开始就控制python脚本的执行,那么只需设置LD_LIBRARY_PATH (在检查它是否已经设置了之后),然后重新执行自己。这就是Java的做法。
B. 你可以通过其绝对路径导入/some/path/xyz.so,然后再导入x.so之前。当你导入x.so时,加载器会发现它已经加载了xyz.so,并且会使用已经加载的模块而不是再次搜索它。
C. 如果你自己构建了x.so,你可以在链接行中添加-Wl,-rpath=/some/path,然后导入x.so将导致加载器在/some/path中查找依赖的模块。

2
@JasonR 可以使用 import dl; dl.open("/some/path/xyz.so") 来实现选项B。 - Employed Russian
4
太好了,谢谢。原来Python 2.6版本以后dl模块已经被弃用,但是你可以使用import ctypes; ctypes.cdll.LoadLibrary("/some/path/xyz.so")来实现相同的功能。 - Jason R
@JasonR 选项B对你有用吗?我尝试使用ctypes导入共享库的完整路径,紧接着导入依赖它的Python模块..但是没有成功.. imp.load_module()仍然失败。 - Gregory
1
@Gregory:是的,它确实做到了。我使用了我在上面评论中指出的语法,使用ctypes,并且对我来说效果很好。你的Python模块可能还依赖于一些没有正确加载的其他共享库。 - Jason R
@JasonR 这取决于很多因素,但它们要么与完整路径链接(标准库),要么具有rpath。那个没有rpath的在导入到Python时总是失败。顺便说一下,它是一个SWIG Python包装共享库,而SWIG建议使用rpath / LD_LIBRARY_PATH,这正是我不想做的,因为它不具备可移植性/可移动性/可复制性。 - Gregory
显示剩余2条评论

16

根据Employed Russian的答案,以下是适用于我的方法。

oracle_libs = os.environ['ORACLE_HOME']+"/lib/"
rerun = True

if not 'LD_LIBRARY_PATH' in os.environ:
  os.environ['LD_LIBRARY_PATH'] = ":"+oracle_libs
elif not oracle_libs in os.environ.get('LD_LIBRARY_PATH'):
  os.environ['LD_LIBRARY_PATH'] += ":"+oracle_libs
else:
  rerun = False

if rerun:
  os.execve(os.path.realpath(__file__), sys.argv, os.environ)

1
execve技巧正是我所需要的,而且它有效,点个赞! - Kenneth Hoste

0
以下代码是用于设置LD_LIBRARY_PATH或任何其他导入模块所需的环境变量路径。
if os.getenv('LD_LIBRARY_PATH')==None: 
    os.environ['LD_LIBRARY_PATH']='<PATH>'
    try:
        sys.stdout.flush()
        os.execl(sys.executable,sys.executable, *sys.argv)
    except OSError as e:
        print(e)
elif <path> not in os.getenv('LD_LIBRARY_PATH'):
    os.environ['LD_LIBRARY_PATH'] = ':'.join([os.getenv('LD_LIBRARY_PATH'),'<PATH>'])
    try:
        sys.stdout.flush()
        os.execl(sys.executable,sys.executable, *sys.argv)
    except OSError as e:
        print(e)

# import X

函数 os.execl 将取代当前进程。在 UNIX 中,将加载新的可执行文件到当前进程中。 通过在导入“X”模块之前添加此代码,现在它将在设置的新路径中查找文件。

更多关于 execl 的信息


0
根据我的经验,尝试在运行中的Python更改加载器的工作方式非常棘手;可能与操作系统/版本有关;可能无法正常工作。一个可能有帮助的解决方法是启动一个子进程,使用shell脚本更改环境参数,然后使用shell启动一个新的Python。

-9

好的,环境变量存储在字典 os.environ 中,所以如果你想要更改,可以这样做

os.environ["PATH"] = "/usr/bin"

我认为你必须确保在导入模块x的语句之前设置os.environ["LD_LIBRARY_PATH"]。 - ThomasH
查看编辑,我已经尝试过这个方法,但没有成功。 - Anurag Uniyal
1
以这种方式设置LD_LIBRARY_PATH不会影响当前进程,正如我在我的答案中所解释的那样。 - Employed Russian

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