我正在尝试找出如何比较2个RPMS列表(当前已安装)和(在本地存储库中可用)并查看哪个RPMS已过期。我一直在修补正则表达式,但RPMS有很多不同的命名标准,我无法获得一个好的列表。我的驱动器上没有实际的RPMS,所以我不能做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
但是除非我打破其他之前有效的其他人,否则这些都不会起作用。
答案 0 :(得分:15)
在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]*
。有关详细信息,请参阅RPM源代码中的lib/rpmvercmp.c
。
答案 1 :(得分:2)
这是一个基于rpmdevtools包中rpmdev-vercmp
的工作程序。您不应该需要任何特殊安装但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)
答案 2 :(得分:1)
更简单的正则表达式是/ ^(。+) - (。+) - (。+)\。(。+)\。rpm $ /
我不知道对包名称的任何限制(第一次捕获)。对版本和发行版的唯一限制是它们不包含“ - ”。没有必要对此进行编码,因为未捕获的' - 将这些字段分开,因此如果确实有' - '它将被拆分而不是单个字段,因此产生的捕获不会包含' - ' 。只有第一个捕获名称包含任何“ - ”,因为它首先消耗所有无关的“ - ”。
然后,有一个体系结构,这个正则表达式假设没有对体系结构名称的限制,除了它不包含'。'。
捕获结果是[名称,版本,发布,拱门]
Owen关于单独依赖rpm名称的回答仍然适用。
现在你必须比较版本字符串,这不是直截了当的。我不相信可以用正则表达式完成。您需要实施比较算法。
答案 3 :(得分:1)
基于Owen S的出色答案,我整理了一个使用系统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绑定不可用。
答案 4 :(得分:0)
RPM具有python绑定,允许您使用rpmUtils.miscutils.compareEVR。元组的第一个和第三个参数是包名称和包装版本。中间是版本。在下面的例子中,我试图弄清楚3.7.4a在哪里排序。
[root@rhel56 ~]# 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
答案 5 :(得分:0)
因为python rpm软件包似乎已经过时并且在pip中不可用;我编写了一个适用于大多数软件包版本的小型实现。包括~
符号周围的魔术。这不会覆盖real implementation的100%,但是可以解决大多数软件包的问题:
def rpm_sort(elements):
""" sort list elements using 'natural sorting': 1.10 > 1.9 etc...
taking into account special characters for rpm (~) """
alphabet = "~0123456789abcdefghijklmnopqrstuvwxyz-."
def convert(text):
return [int(text)] if text.isdigit() else ([alphabet.index(letter) for letter in text.lower()] if text else [1])
def alphanum_key(key):
return [convert(c) for c in re.split('([0-9]+)', key)]
return sorted(elements, key=alphanum_key)
已测试:
rpms = ['my-package-0.2.1-0.dev.20180810',
'my-package-0.2.2-0~.dev.20181011',
'my-package-0.2.2-0~.dev.20181012',
'my-package-0.2.2-0',
'my-package-0.2.2-0.dev.20181217']
self.assertEqual(rpms, rpm_sort(rpms))
未涵盖
目前只有一种情况我不知道,但可能会弹出其他情况:word~
> word
,而根据rpm规范,反数应为真(任何单词结尾加上字母,最后是~
)