如何比较python中的Rpm版本

2024-09-27 09:32:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图找出如何比较两个rpm列表(当前已安装)和(在本地存储库中可用)并查看哪些rpm已过期。我一直在修改regex,但是有太多不同的RPMS命名标准,我无法得到一个好的列表来使用。我的驱动器上没有实际的rpm,所以我不能做rpm-qif。

pattern1 = re.compile(r'^([a-zA-Z0-9_\-\+]*)-([a-zA-Z0-9_\.]*)-([a-zA-Z0-9_\.]*)\.(.*)')
for rpm in listOfRpms:
     packageInfo = pattern1.search(rpm[0]).groups()
     print packageInfo

这适用于绝大多数人,但不是所有人(2300/2400)

  yum-metadata-parser-1.1.2-2.el5
('yum-metadata-parser', '1.1.2', '2', 'el5') **What I need

但这些都不起作用,比如说,除非我打破了以前起作用的其他一些。。

  • wvdial-1.54.0-3型
  • xdelta-1.1.3-20型
  • xdelta-1.1.3-20平方米
  • xmlsec1-1.2.6-3号文件
  • xmlsec1-1.2.6-3二
  • ypbind-1.17.2-13型
  • ypbind-1.17.2-8型
  • ypserv-2.13-14公司
  • 邮编:2.3-27
  • zlib-1.2.3-3号
  • zlib-1.2.3-3_2号
  • zsh-4.2.6-1型

Tags: parser列表regexmetadatarpmyumzlibza
3条回答

用RPM术语来说,2.el5是发布字段;2和el5不是单独的字段。但是,正如您的示例所示,release不需要包含.。从末尾放下\.(.*),一次捕获释放字段。

所以现在你有了一个包的名称,版本和发行版。比较它们的最简单方法是使用rpm的python模块:

import rpm
# t1 and t2 are tuples of (version, release)
def compare(t1, t2):
    v1, r1 = t1
    v2, r2 = t2
    return rpm.labelCompare(('1', v1, r1), ('1', v2, r2))

你问,额外的'1'是什么?这是epoch,它覆盖了其他版本比较考虑事项。而且,它通常在文件名中不可用。在这里,为了这个练习的目的,我们把它伪装成'1',但这可能一点也不准确。这是如果只使用文件名,逻辑将关闭的两个原因之一。

逻辑可能与rpm不同的另一个原因是Obsoletes字段,该字段允许将包升级为具有完全不同名称的包。如果你能接受这些限制,那就继续吧。

如果手头没有rpmpython库,下面是比较rpm 4.4.2.3的每个版本、版本和纪元的逻辑:

  • 在每个字符串中搜索由垃圾邮件分隔的字母字段[a-zA-Z]+和数字字段[0-9]+
  • 每个字符串中的连续字段相互比较。
  • 字母部分按字典顺序比较,数字部分按数字顺序比较。
  • 如果一个字段是数字,一个是字母,则在不匹配的情况下,数字字段总是被认为更大(更新)。
  • 在一个字符串用完字段的情况下,另一个总是被认为更大(更新)。

有关血淋淋的详细信息,请参见RPM源中的lib/rpmvercmp.c

这是一个基于rpmdevtools包中rpmdev-vercmp的工作程序。除了yum(它提供了rpmUtils.miscutilspython模块)之外,您不需要安装任何特殊的东西。

与其他答案相比,它的优点是您不需要解析任何内容,只需输入完整的RPM名称版本字符串,如:

$ ./rpmcmp.py bash-3.2-32.el5_9.1 bash-3.2-33.el5.1
0:bash-3.2-33.el5.1 is newer
$ echo $?
12

退出状态11表示第一个较新,12表示第二个较新。

#!/usr/bin/python

import rpm
import sys
from rpmUtils.miscutils import stringToVersion

if len(sys.argv) != 3:
    print "Usage: %s <rpm1> <rpm2>"
    sys.exit(1)

def vercmp((e1, v1, r1), (e2, v2, r2)):
    return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))

(e1, v1, r1) = stringToVersion(sys.argv[1])
(e2, v2, r2) = stringToVersion(sys.argv[2])

rc = vercmp((e1, v1, r1), (e2, v2, r2))
if rc > 0:
    print "%s:%s-%s is newer" % (e1, v1, r1)
    sys.exit(11)

elif rc == 0:
    print "These are equal"
    sys.exit(0)

elif rc < 0:
    print "%s:%s-%s is newer" % (e2, v2, r2)
    sys.exit(12)

基于Owen的出色回答,我整理了一个片段,它使用系统RPM绑定(如果可用),但返回到基于正则表达式的仿真:

try:
    from rpm import labelCompare as _compare_rpm_labels
except ImportError:
    # Emulate RPM field comparisons
    #
    # * Search each string for alphabetic fields [a-zA-Z]+ and
    #   numeric fields [0-9]+ separated by junk [^a-zA-Z0-9]*.
    # * Successive fields in each string are compared to each other.
    # * Alphabetic sections are compared lexicographically, and the
    #   numeric sections are compared numerically.
    # * In the case of a mismatch where one field is numeric and one is
    #   alphabetic, the numeric field is always considered greater (newer).
    # * In the case where one string runs out of fields, the other is always
    #   considered greater (newer).

    import warnings
    warnings.warn("Failed to import 'rpm', emulating RPM label comparisons")

    try:
        from itertools import zip_longest
    except ImportError:
        from itertools import izip_longest as zip_longest

    _subfield_pattern = re.compile(
        r'(?P<junk>[^a-zA-Z0-9]*)((?P<text>[a-zA-Z]+)|(?P<num>[0-9]+))'
    )

    def _iter_rpm_subfields(field):
        """Yield subfields as 2-tuples that sort in the desired order

        Text subfields are yielded as (0, text_value)
        Numeric subfields are yielded as (1, int_value)
        """
        for subfield in _subfield_pattern.finditer(field):
            text = subfield.group('text')
            if text is not None:
                yield (0, text)
            else:
                yield (1, int(subfield.group('num')))

    def _compare_rpm_field(lhs, rhs):
        # Short circuit for exact matches (including both being None)
        if lhs == rhs:
            return 0
        # Otherwise assume both inputs are strings
        lhs_subfields = _iter_rpm_subfields(lhs)
        rhs_subfields = _iter_rpm_subfields(rhs)
        for lhs_sf, rhs_sf in zip_longest(lhs_subfields, rhs_subfields):
            if lhs_sf == rhs_sf:
                # When both subfields are the same, move to next subfield
                continue
            if lhs_sf is None:
                # Fewer subfields in LHS, so it's less than/older than RHS
                return -1
            if rhs_sf is None:
                # More subfields in LHS, so it's greater than/newer than RHS
                return 1
            # Found a differing subfield, so it determines the relative order
            return -1 if lhs_sf < rhs_sf else 1
        # No relevant differences found between LHS and RHS
        return 0


    def _compare_rpm_labels(lhs, rhs):
        lhs_epoch, lhs_version, lhs_release = lhs
        rhs_epoch, rhs_version, rhs_release = rhs
        result = _compare_rpm_field(lhs_epoch, rhs_epoch)
        if result:
            return result
        result = _compare_rpm_field(lhs_version, rhs_version)
        if result:
            return result
        return _compare_rpm_field(lhs_release, rhs_release)

请注意,我还没有对这个与C级实现的一致性进行过广泛的测试——我只是将它用作一个后备实现,它至少足以让Anitya的测试套件在系统RPM绑定不可用的环境中通过。

相关问题 更多 >

    热门问题