2010-07-08 154 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綁定不可用的環境中通過。