在Python中导入前设置LD_LIBRARY_PATH

62 投票
5 回答
119902 浏览
提问于 2025-04-18 03:55

Python使用一个叫做PYTHONPATH的环境变量来决定它应该在哪些文件夹里寻找模块。你可以通过修改sys.path来进行一些尝试,这对于纯Python模块来说效果很好。

但是,如果一个模块使用了共享对象文件或静态库,它会在LD_LIBRARY_PATH中寻找这些文件(在Linux系统上)。不过,这个路径不太好更改,而且根据不同的平台也会有所不同。

解决这个问题的一个快速方法是设置环境变量,或者像这样运行脚本:LD_LIBRARY_PATH=. ./script.py,但这样的话,每次打开新的终端时你都得重新设置一次。

另外,在我的情况下,.so文件总是和.py文件在同一个目录里,但它们也可能会被移动到其他绝对路径,所以我希望每次运行脚本时都能自动设置这些路径。

我该如何在运行时独立于平台地编辑Python解释器查找库的路径呢?

编辑:

我已经尝试过os.environ['LD_LIBRARY_PATH'] = os.getcwd(),但没有成功。

5 个回答

2

从coreutils 8.30版本开始,你可以使用 env -S 来把脚本的开头那一行(也叫shebang行)分成不同的参数:

#!/usr/bin/env -S LD_LIBRARY_PATH=/path/to/lib python options

为了兼容老旧的系统,你可以利用一个特点:在shell中,所有命令都可以用引号括起来,而在python中,这些命令就只是字符串而已:

#!/bin/sh
"export" "LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH"
"exec" "python3" "$0" "$@"
# Further python program
import somemodule
3

这个解决方案在环境重新初始化的时候效果很好。

import os

os.environ['LD_LIBRARY_PATH'] = os.getcwd()  # or whatever path you want 

这段代码需要在合适的地方使用……

os.execv(sys.argv[0], sys.argv)
14

在Python中,当你获取环境变量的值,比如用 os.environ['LD_LIBRARY_PATH']os.environ['PATH'] 时,实际上是从它的父进程(通常是bash)那里复制这些值到一个字典里。也就是说,bash进程的环境变量会传递给运行中的Python实例。

你可以通过在bash中输入 env 命令来查看这些环境变量。

另外,你也可以通过访问 /proc/<pid>/environ 来查看这些环境数据。如果你在修改任何环境变量后引入一个无限循环(比如 while 1: pass),你就能看到这些数据。

如果你在Python脚本中修改了环境变量,然后去 /proc/<pid>/environ 查看这个变量的值,你会发现实际的变量数据并没有被修改,尽管Python脚本显示的字典中的值是更新过的。

实际上,当你在Python脚本中修改一个环境变量,比如 os.environ['LD_LIBRARY_PATH']='/<new_location>',只是更新了本地字典中的值,而这个字典并没有映射到进程的环境变量部分。因此,这个修改不会反映到当前进程的环境中,因为只有本地字典被修改了。

所以,如果我们想让新的环境变量生效,就需要使用 execv 来覆盖进程的内存映像,更新环境变量的数据。

例子:

new_lib = '/<new_location>'
if not new_lib in os.environ['LD_LIBRARY_PATH']:
    os.environ['LD_LIBRARY_PATH'] += ':'+new_lib
    try:
        os.execv(sys.argv[0], sys.argv)
    except Exception as e:
        sys.exit('EXCEPTION: Failed to Execute under modified environment, '+e)

import xyz
#do something else

限制:理想情况下,Python不应该允许修改 os.environ 变量。但由于没有常量字典数据类型,它允许对数据变量进行修改。实际上,修改这些值没有什么用,因为它不会对运行中的进程的真实环境产生任何影响,除非使用 execv

20

我解决这个问题的方法是在Python脚本的第一行加上这个内容(而不是通常的shebang):

exec env LD_LIBRARY_PATH=/some/path/to/lib /path/to/specific/python -x "$0" "$@"

下面是这个方法的工作原理:

  • 如果没有shebang,当前的命令行会把这个文件当成一个shell脚本来处理,
  • “exec”确保这一行也是这个文件中由shell执行的最后一条命令,
  • 这里的“env”用来设置一些环境变量,比如LD_LIBRARY_PATH,
  • 可以指定Python解释器的确切路径,或者“env”可以在PATH中找到它,
  • “-x”是Python的一个选项,它会让Python解释器忽略第一行,
  • “$0”代表脚本的名字,而“$@”会被位置参数替代。
30

更新:请查看下面的编辑内容。

我会使用:

import os

os.environ['LD_LIBRARY_PATH'] = os.getcwd()  # or whatever path you want

这段代码只会在当前程序运行期间设置LD_LIBRARY_PATH这个环境变量。

编辑:看起来这个变量需要在启动Python之前设置:在运行时更改LD_LIBRARY_PATH以适应ctypes

所以我建议你可以写一个包装的.sh脚本(如果你坚持的话,也可以用.py脚本)。另外,正如@chepner提到的,你可能还想考虑把你的.so文件安装到一个标准的位置(在虚拟环境内)。

另请参见从Python内部设置LD_LIBRARY_PATH

撰写回答