如何从Python包文件名中准确提取包名与版本号?
从Python包文件名提取包名与版本号的可靠Bash方案
痛点解析
Python包文件名(如whl、tar.gz)的结构看似规律,但包名可能包含数字、下划线/连字符混用,版本号遵循PEP 440规范(支持.dev、.post、rc等后缀),常规的cut/grep/sed分割逻辑很容易在certifi-2024.2.2、charset-normalizer-3.3.2这类案例上出错——要么误把版本号的部分字符当成包名,要么截断版本号。
核心正则逻辑
基于PEP 426的包命名规范,文件名的核心结构为:{包名}-{版本号}-[平台信息].{后缀}。我们可以用正则精准捕获包名和版本号:
- 包名:由字母、数字、下划线、连字符组成,直到遇到以数字开头的版本号
- 版本号:以数字开头,包含字母、数字、点、下划线、连字符,覆盖所有PEP 440格式
- 忽略版本号后的平台信息和后缀
可直接使用的Bash脚本
提取函数
extract_pkg_info() { local filename="$1" # 正则匹配:捕获包名、版本号,兼容whl/tar.gz/zip后缀 if [[ "$filename" =~ ^([a-zA-Z0-9_\-]+)-([0-9][0-9a-zA-Z_\.\-]*)(?:-[^.]*)?\.(whl|tar\.gz|zip)$ ]]; then local pkg_name="${BASH_REMATCH[1]}" local pkg_version="${BASH_REMATCH[2]}" # 可选:将下划线转为连字符(部分包文件名用下划线替代标准连字符) pkg_name="${pkg_name//_/-}" echo "包名: $pkg_name | 版本号: $pkg_version" else echo "⚠️ 无法解析: $filename" fi }
测试验证
针对之前失败的案例,测试结果如下:
- 输入:
certifi-2024.2.2-py3-none-any.whl→ 输出:包名: certifi | 版本号: 2024.2.2 - 输入:
charset-normalizer-3.3.2.tar.gz→ 输出:包名: charset-normalizer | 版本号: 3.3.2 - 输入:
charset_normalizer-3.3.2-py3-none-any.whl→ 输出:包名: charset-normalizer | 版本号: 3.3.2 - 输入:
scikit-learn-1.4.1.post1-cp310-cp310-manylinux_2_17_x86_64.whl→ 输出:包名: scikit-learn | 版本号: 1.4.1.post1 - 输入:
pytest-8.1.0rc1-py3-none-any.whl→ 输出:包名: pytest | 版本号: 8.1.0rc1
批量处理目录下的包文件
# 遍历当前目录下所有包文件 for pkg_file in *.whl *.tar.gz *.zip; do [[ -f "$pkg_file" ]] && extract_pkg_info "$pkg_file" done
关键细节说明
- 正则非捕获组:
(?:-[^.]*)?用于匹配版本号后的平台信息(如-py3-none-any),但不捕获,避免干扰包名和版本号的提取 - 版本号匹配规则:
[0-9][0-9a-zA-Z_\.\-]*确保版本号以数字开头,兼容所有PEP 440允许的格式(包括预发布版本、.post后缀等) - 下划线转连字符:可选步骤,因为部分包的文件名用下划线(如
charset_normalizer),但PyPI上的正式包名是连字符,根据需求可删除该逻辑
内容的提问来源于stack exchange,提问作者Stephen Paulin
相关产品推荐
相关产品推荐

