Linux下通过Python代码获取已安装库列表的方法

Evan
Evan
发布时间:2026-11-06| 9人浏览过
原创

详细介绍在Linux操作系统下使用Python原生代码获取当前环境已安装第三方库与包版本列表的多种方案,深入解析importlib.metadata标准库原理、与pip子进程的对比,以及在Debian/Ubuntu dist-packages和虚拟环境下的最佳实践。

LINUX · PYTHON 运行时自省技术指南
执行环境:Linux (Ubuntu / Debian / RHEL / Alpine) 推荐方案:Python 3.8+ 标准库 外部依赖:零外部依赖 (Zero Dependencies)
Linux下通过Python代码获取已安装库列表的方法
图 0:Linux 下通过 Python 代码获取已安装库列表方案全景缩略图

在 Linux 环境下,获取当前 Python 运行时已安装的第三方库及其版本信息,最直接且推荐的标准做法是使用 Python 3.8+ 内置的 importlib.metadata 标准库。该模块无需调用外部 shell 命令、无需额外安装第三方依赖,通过直接解析文件系统中的分发包元数据(.dist-info 与 .egg-info),能够毫秒级返回宿主系统(如 Debian/Ubuntu 的 dist-packages)或项目专属虚拟环境(site-packages)中所有合法的已安装库列表。对于需要完全对齐 pip list 专有特性(如可编辑安装目录)的场景,也可通过 subprocess 明确绑定当前解释器进行读取。

一、首选核心方案:使用内置标准库 importlib.metadata

从 Python 3.8 开始,官方标准库正式引入了 importlib.metadata(基于 PEP 566、PEP 590 和 PEP 621 标准)。它提供了一组纯 Python API,用于在运行时查询已安装分发包(Distributions)的元数据。该方案不导入目标模块本身,因此绝不会触发包初始化代码中的隐式计算或网络连接,具备高安全性和极佳的执行效率。

Linux下通过Python代码获取已安装库列表的方法
图 1:Python 代码读取 Linux 环境已安装库的三步标准操作流程

1. 基础实现:遍历已安装包与版本

调用 importlib.metadata.distributions() 会返回一个可迭代的生成器,每个元素为一个 Distribution 对象。可以通过属性 name(或读取其 metadata["Name"])和 version 提取对应信息:

import sys
import importlib.metadata

# 1. 验证当前 Python 解释器与执行路径
print(f"当前解释器: {sys.executable}")

# 2. 检索并按包名排序已安装库列表
distributions = importlib.metadata.distributions()
sorted_dists = sorted(distributions, key=lambda d: d.metadata["Name"].lower())

for dist in sorted_dists:
    pkg_name = dist.metadata["Name"]
    pkg_version = dist.version
    print(f"{pkg_name}=={pkg_version}")

该脚本在 Linux 终端执行后的示例输出如下(标记为示例):

# [示例输出 - 非实际运行]
当前解释器: /usr/bin/python3
certifi==2024.2.2
cryptography==42.0.5
idna==3.6
requests==2.31.0
urllib3==2.2.1
wheel==0.43.0

2. 结构化封装:导出为字典或 JSON 文本

在实际生产服务、自动化运维探针或健康检查接口中,通常需要将安装库列表结构化为字典数组或直接转换为 JSON 格式,便于日志归档或跨服务传输:

import json
import importlib.metadata

def get_installed_packages():
    """获取当前环境中所有已安装库,返回字典列表"""
    packages = []
    seen = set()  # 防止路径重复带来的冗余记录

    for dist in importlib.metadata.distributions():
        name = dist.metadata["Name"]
        if name.lower() in seen:
            continue
        seen.add(name.lower())
        packages.append({
            "name": name,
            "version": dist.version,
            "summary": dist.metadata.get("Summary", "")
        })

    return sorted(packages, key=lambda x: x["name"].lower())

if __name__ == "__main__":
    pkg_list = get_installed_packages()
    print(json.dumps(pkg_list, indent=2, ensure_ascii=False))

二、Linux 系统下的 Python 库存储机制与搜索定位原理

要理解 importlib.metadata 为何能在 Linux 下准确列出所有包,必须明确 Linux 文件系统中 Python 库的实际物理存储组织方式。

Linux下通过Python代码获取已安装库列表的方法
图 2:Linux 环境下 Python 库搜索路径与分发包元数据定位机制

1. site-packages 与 dist-packages 的本质区别

在标准的 Linux 发行版(特别是 Debian、Ubuntu 以及 Linux Mint)中,系统预装的 Python 存在一个显著特征:它将官方仓库(通过 apt install python3-xxx)分发的库放在 /usr/lib/python3/dist-packages 目录下,而将非系统托管目录命名为 site-packages。

  • dist-packages:Debian 系 Linux 专有目录。由 Linux 系统的包管理器(apt/dpkg)统一管控,目的是避免系统管理脚本使用的 Python 库被用户随意的 pip 操作意外覆盖破坏。
  • site-packages:Python 官方默认的标准第三方库存储目录。在虚拟环境(venv)、用户主目录(~/.local/lib/python3.x/site-packages)或 RedHat/CentOS/Fedora 系统中普遍使用。

importlib.metadata 在底层并不会硬编码某个具体目录,而是以 sys.path 的内容为基准,依次扫描其中的每一个目录,寻找符合规范的 .dist-info 或 .egg-info 文件夹。因此,不论库位于 dist-packages 还是 site-packages,均能被自动解析,无需人工区分。

2. 核心区别:已安装库列表与 sys.modules 内存缓存

部分刚接触 Python 运行时自省的开发者常误用 sys.modules.keys() 来获取已安装库,这是常见的设计误区:

  • sys.modules 是 Python 解释器在当前进程运行期间已经载入内存的模块映射表。未被 import 引入的代码绝对不会出现在其中。
  • importlib.metadata.distributions() 则遍历磁盘安装路径,读取所有符合分发规范的包描述,即便当前程序没有任何 import 语句,也能完整获知环境中已就绪的所有分发包。

三、替代方案与历史旧方案对比分析

除了 importlib.metadata 之外,在特定工程背景或维护遗留项目时,还可以见到另外两种获取方式。它们在实现原理、环境依赖与运行开销上存在明显差异。

Linux下通过Python代码获取已安装库列表的方法
图 3:三种获取已安装库技术方案的横向参数与适用条件对比

1. 子进程调用:绑定 sys.executable 执行 pip list

若业务要求与命令行 pip list 的输出格式严格对齐,或者需要识别以 -e / --editable 模式开发的源码链接包,可以通过 subprocess 派生子进程调用 pip 工具:

import sys
import json
import subprocess

def get_packages_via_pip():
    # 关键注意:使用 sys.executable 锁定当前正在运行的 Python 实例
    cmd = [sys.executable, "-m", "pip", "list", "--format=json"]
    try:
        proc = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            check=True
        )
        return json.loads(proc.stdout)
    except (subprocess.CalledProcessError, FileNotFoundError) as e:
        print(f"调用 pip 失败: {e}", file=sys.stderr)
        return []

if __name__ == "__main__":
    packages = get_packages_via_pip()
    for pkg in packages[:5]:
        print(f"{pkg['name']} -> {pkg['version']}")
⚠️ Linux 下子进程调用的关键注意事项

在编写 Linux 脚本时,绝不可直接硬编码命令字符串 ["pip", "list"]。因为在多版本 Python 并存(如系统同时存在 Python 3.8、3.10、3.12)或激活虚拟环境的场景下,环境变量 $PATH 找到的 pip 往往与当前运行代码的解释器不属于同一环境。务必显式传入 sys.executable 启动对应模块。

2. 历史方案:pkg_resources 的局限与废弃

在 Python 2.x 以及 Python 3.7 及更早版本中,广泛使用的是 setuptools 附带的 pkg_resources.working_set:

# 历史兼容代码(不推荐在新代码中使用)
import pkg_resources

installed = [
    {"name": dist.project_name, "version": dist.version}
    for dist in pkg_resources.working_set
]

尽管这一方式在老旧 Linux 脚本中仍有残留,但官方已在 setuptools 67+ 及 PEP 594 中正式将其列为废弃状态(Deprecated)。它的致命缺点是在 import pkg_resources 时会同步启动深度解析,带来长达数十至数百毫秒的冷启动延迟,并在部分轻量 Linux 容器中因缺少 setuptools 包直接引发 ModuleNotFoundError。

四、Linux 生产环境常见边界条件与避坑指南

在 Linux 服务器、Docker 容器或 CI/CD 自动化流水线中运行自省代码时,需特别注意以下几项边界条件与排查要点:

Linux下通过Python代码获取已安装库列表的方法
图 4:Linux 下 Python 库自省技术决策路径与生产环境避坑清单

1. PEP 668(EXTERNALLY-MANAGED)下的只读安全性

在现代 Linux 发行版(如 Ubuntu 23.04+、Debian 12+、Fedora 38+)中,系统 Python 默认启用了 PEP 668 保护,存在 /usr/lib/python3.x/EXTERNALLY-MANAGED 标记文件。此时如果运行 pip install 会被系统直接拦截并报错。

但请注意:PEP 668 仅限制安装和修改行为,绝不限制读取操作。使用 importlib.metadata.distributions() 是完全只读的文件头解析过程,在任何开启了 PEP 668 的系统上均能安全稳定运行,不受任何策略拦截。

2. 极简容器(Minimal Docker Images)的依赖陷阱

基于 Alpine Linux、Ubuntu Minimal 或 Debian Slim 构建的生产级 Docker 镜像,通常为了减小体积在镜像制作阶段删除了 pip 工具及 setuptools。

  • 如果你的代码采用 subprocess.run(["pip", ...]),在此类镜像中将直接抛出 FileNotFoundError 导致服务崩溃。
  • 而 importlib.metadata 是 Python 标准库的一部分,只要镜像中存在 Python 解释器即可运行,是极简容器化环境中唯一具备零依赖保障的可靠方案。

3. 虚拟环境(Virtualenv)与系统全局库的隔离验证

在 Linux 部署应用时,强烈推荐使用 venv 创建项目隔离环境。通过 Python 代码检查当前是否处于隔离环境,可通过对比 sys.prefix 与 sys.base_prefix 实现:

import sys
import importlib.metadata

is_venv = (sys.prefix != getattr(sys, "base_prefix", sys.prefix))
print(f"是否在虚拟环境中: {is_venv}")
print(f"当前安装前缀: {sys.prefix}")

# 在虚拟环境中,distributions() 会默认优先检索该虚拟环境下的 site-packages
total_dists = len(list(importlib.metadata.distributions()))
print(f"当前上下文可访问库总数: {total_dists}")

五、方案选型速查与总结

综合对比各技术路线在 Linux 平台下的表现,开发与运维人员应根据具体约束做出明确选型:

选型维度 标准库 importlib.metadata 子进程 sys.executable -m pip 传统 pkg_resources
Python 版本 Python 3.8+(当前主流) 与 pip 兼容的全部版本 遗留 Python 2.7 / 3.x
外部依赖 完全零依赖 依赖环境中安装了 pip 强依赖 setuptools
执行耗时 约 5 ~ 20 毫秒 约 150 ~ 400 毫秒 约 80 ~ 200 毫秒
极简容器适配 极佳(原生支持) 差(常缺失 pip 二进制) 较差(常缺失 setuptools)
推荐指数 ⭐⭐⭐⭐⭐(首选) ⭐⭐⭐⭐(特定运维工具) ⭐(不建议使用)

核心结论:对于现代 Linux 生产环境下的 Python 应用,直接采用内置的 importlib.metadata.distributions() 是最简洁、稳健且性能最高的方式;它全面兼容 Debian 系的 dist-packages 与常规 site-packages,在容器只读模式及 PEP 668 环境下均能提供确定性的结果。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。