2010-07-08 23 views
11

我想了解如何比较RPMS(当前安装)和(可在本地存储库)的2个列表,并查看哪些RPMS过期。我一直在修正正则表达式,但RPMS有很多不同的命名标准,所以我无法得到一个好的列表来处理。我的驱动器上没有实际的RPMS,所以我无法执行rpm -qif。我如何比较Python中的Rpm版本

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 

这适用于绝大多数但不是所有(二千四百分之二千三百)

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_2
  • xmlsec1-1.2.6-3
  • xmlsec1-1.2.6-3_2
  • 的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
+0

你如何获得RPM列表? – Craig 2010-07-08 17:34:38

回答

14

在RPM的说法中,2.el5是发布字段; 2和el5不是独立的字段。但是,正如您的示例所示,发行版本中不需要有.。从尾部删除\.(.*)以一次捕获释放字段。

所以现在你有一个包的名称,版本和版本。对它们进行比较的最简单方法是使用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',你问?这是时代,它覆盖了其他版本比较的考虑。此外,它通常在文件名中不可用。在这里,我们假设这个练习的目的是'1',但这可能并不准确。如果你单独使用文件名,这是你的逻辑关闭的两个原因之一。

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

如果你没有在手rpm Python库,这里的逻辑,每一个版本,版本和时代的比较作为rpm 4.4.2.3

  • 搜索每串字母领域[a-zA-Z]+和数字领域[0-9]+被垃圾分隔[^a-zA-Z0-9]*
  • 每个字符串中的连续字段相互比较。
  • 按字母顺序比较按字母顺序排列的部分,并将数字部分进行数字比较。
  • 在不匹配的情况下,一个字段是数字,一个是字母,数字字段总是被认为更大(更新)。
  • 在一个字符串用完字段的情况下,另一个字段总是被认为更大(更新)。

请参阅lib/rpmvercmp.c在RPM来源的血淋淋的细节。

+0

非常感谢Owen S.我之前正在研究rpm python模块,但因为我认为它只与RPM数据库交互,所以将其解开。奇迹般有效!我只有文件名,因为我从一个zenoss服务器上提取了RPM列表,并将它与本地镜像列表进行比较。过时不是我的要求。 – Adam 2010-07-08 19:27:55

+1

感谢您的支持!我将您的算法描述转换为http://stackoverflow.com/a/42967591/597742中的纯Python后备 – ncoghlan 2017-03-23 05:06:01

0

一个更简单的正则表达式是/^(.+)-(.+)-(.+).(.+).rpm$/

我不知道的对任何限制包名称(第一次捕获)。版本和版本的唯一限制是它们不包含' - '。没有必要对此进行编码,因为未捕获的''将这些区域分开,因此如果其中一个' - '将被拆分并且不是单个区域,则结果捕获将不包含' - ' 。只有第一个捕获名称包含任何' - ',因为它首先会消耗所有无关的' - '。

然后,有一个体系结构,这个正则表达式不会限制体系结构名称,只是它不包含'。'。

捕获结果[名称,版本,发布,弓]

从欧文的关于依靠转名回答注意事项单独仍然适用。

0

RPM具有python绑定,它允许您使用rpmUtils.miscutils.compareEVR。元组的第一个和第三个参数是包名和包装版本。中间是版本。在下面的例子中,我试图找出3.7.4a在哪里排序。

[[email protected] ~]# python 
Python 2.4.3 (#1, Dec 10 2010, 17:24:35) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import rpmUtils.miscutils 
>>> rpmUtils.miscutils.compareEVR(("foo", "3.7.4", "1"), ("foo", "3.7.4", "1")) 
0 
>>> rpmUtils.miscutils.compareEVR(("foo", "3.7.4", "1"), ("foo", "3.7.4a", "1")) 
-1 
>>> rpmUtils.miscutils.compareEVR(("foo", "3.7.4a", "1"), ("foo", "3.7.4", "1")) 
1 
2

下面是基于关闭的rpmdev-vercmprpmdevtools包工作程序。你不应该需要任何特殊的安装,但yum(它提供了rpmUtils.miscutils python模块)让它工作。

优于其他的答案是,你不需要任何解析出来,只给它全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) 
1

基于欧文小号的出色答卷,我放在一起如果有需要使用系统的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) 

注意,我避风港't为了与C级实现保持一致性而进行了广泛的测试 - 我仅将它用作回退实现,它至少足以让Anitya的测试套件在系统RPM绑定不可用的环境中通过。