Take the 2-minute tour ×

I'm trying to find out how I can compare 2 lists of RPMS (Currently installed) and (Available in local repository) and see which RPMS are out of date. I've been tinkering with regex but there are so many different naming standards for RPMS that i can't get a good list to work with. I don't have the actual RPMS on my drive so i can't do 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

This works for a vast majority but not all (2300 / 2400)

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

But none these work for instance unless I break some others that worked before..

  • 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
  • zip-2.3-27
  • zlib-1.2.3-3
  • zlib-1.2.3-3_2
  • zsh-4.2.6-1
share|improve this question
how are you getting the list of RPMs? –  Craig Jul 8 '10 at 17:34

4 Answers 4

up vote 9 down vote accepted

In RPM parlance, 2.el5 is the release field; 2 and el5 are not separate fields. However, release need not have a . in it as your examples show. Drop the \.(.*) from the end to capture the release field in one shot.

So now you have a package name, version, and release. The easiest way to compare them is to use rpm's python module:

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))

What's that extra '1', you ask? That's epoch, and it overrides other version comparison considerations. Further, it's generally not available in the filename. Here, we're faking it to '1' for purposes of this exercise, but that may not be accurate at all. This is one of two reasons your logic is going to be off if you're going by file names alone.

The other reason that your logic may be different from rpm's is the Obsoletes field, which allows a package to be upgraded to a package with an entirely different name. If you're OK with these limitations, then proceed.

If you don't have the rpm python library at hand, here's the logic for comparing each of release, version, and epoch as of rpm

  • 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).

See lib/rpmvercmp.c in the RPM source for the gory details.

share|improve this answer
Thanks alot Owen S. I was looking into the rpm python module earlier but dismissed it because I thought it only interacted with the RPM database. Works like a charm! I only have file names because I'm pulling a list of RPMs from a zenoss server and comparing it to a list on a local mirror. Obsoletes are not a requirement for me. –  Adam Jul 8 '10 at 19:27

A much simpler regex is /^(.+)-(.+)-(.+).(.+).rpm$/

I'm not aware of any restrictions on the package name (first capture). The only restrictions on version and release are that they do not contain '-'. There is no need to code this, as the uncaptured '-'s separate those fields, thus if one did have a '-' it would be split and not be a single feild, ergo the resulting capture would not contain a '-'. Only the first capture, the name, contains any '-' because it consumes all extraneous '-' first.

Then, there's the architecture, which this regex assumes no restriction on the architecture name, except that it not contain a '.'.

The capture results are [name, version, release, arch]

Caveats from Owen's answer about relying on the rpm name alone still apply.

share|improve this answer

RPM has python bindings, which lets you use rpmUtils.miscutils.compareEVR. The first and third arguments of the tuple are the package name and the packaging version. The middle is the version. In the example below, I'm trying to figure out where 3.7.4a gets sorted.

[root@rhel56 ~]# python
Python 2.4.3 (#1, Dec 10 2010, 17:24:35) 
>>> import rpmUtils.miscutils
>>> rpmUtils.miscutils.compareEVR(("foo", "3.7.4", "1"), ("foo", "3.7.4", "1"))
>>> rpmUtils.miscutils.compareEVR(("foo", "3.7.4", "1"), ("foo", "3.7.4a", "1")) 
>>> rpmUtils.miscutils.compareEVR(("foo", "3.7.4a", "1"), ("foo", "3.7.4", "1")) 
share|improve this answer

Here's a working program based off of rpmdev-vercmp from the rpmdevtools package. You shouldn't need anything special installed but yum (which provides the rpmUtils.miscutils python module) for it to work.

The advantage over the other answers is you don't need to parse anything out, just feed it full RPM name-version strings like:

$ ./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 $?

Exit status 11 means the first one is newer, 12 means the second one is newer.


import rpm
import sys
from rpmUtils.miscutils import stringToVersion

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

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)

elif rc == 0:
    print "These are equal"

elif rc < 0:
    print "%s:%s-%s is newer" % (e2, v2, r2)
share|improve this answer

Your Answer


