Wie kann ich Rpm Versionen in Python vergleichen?

Ich versuche herauszufinden, wie ich 2 Listen von RPMS (derzeit installiert) und (im lokalen Repository verfügbar) vergleichen kann und welche RPMS nicht mehr aktuell sind. Ich habe mit Regex basteln, aber es gibt so viele verschiedene Namensnormen für RPMS, dass ich keine gute Liste mit der Arbeit bekommen kann. Ich habe nicht die tatsächlichen RPMS auf meinem Laufwerk so kann ich nicht 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 

Das funktioniert für eine große Mehrheit, aber nicht alle (2300/2400)

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

Aber keine diese Arbeit zum Beispiel, es sei denn, ich breche einige andere, die vorher gearbeitet haben ..

  • 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

5 Solutions collect form web for “Wie kann ich Rpm Versionen in Python vergleichen?”

In RPM 2.el5 , 2.el5 ist das Release-Feld; 2 und el5 sind keine separaten Felder. Allerdings muss Release nicht haben . In ihr, wie Ihre Beispiele zeigen. Drop die \.(.*) Von Ende, um das Release-Feld in einem Schuss zu erfassen.

So, jetzt haben Sie einen Paketnamen, Version und Release. Der einfachste Weg, sie zu vergleichen, ist die Verwendung von rpm's Python-Modul:

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

Was ist das extra '1' , fragen Sie? Das ist Epoche, und es überschreibt andere Versionsvergleichsbetrachtungen. Außerdem ist es im Allgemeinen nicht im Dateinamen verfügbar. Hier fälschen wir es zu "1" für die Zwecke dieser Übung, aber das ist vielleicht nicht genau. Dies ist einer von zwei Gründen, die deine Logik ausschaltet, wenn du allein mit Dateinamen gehst.

Der andere Grund, dass Ihre Logik von rpm 's unterscheiden kann, ist das Obsoletes Feld, das es ermöglicht, dass ein Paket auf ein Paket mit einem ganz anderen Namen aktualisiert wird. Wenn Sie mit diesen Einschränkungen OK sind, dann fahren Sie fort.

Wenn Sie nicht die rpm Python-Bibliothek zur Hand haben, hier ist die Logik für den Vergleich jeder von Release, Version und Epoche ab rpm 4.4.2.3 :

  • Suchen Sie jede Zeichenkette für alphabetische Felder [a-zA-Z]+ und numerische Felder [0-9]+ getrennt durch Junk [^a-zA-Z0-9]* .
  • Aufeinanderfolgende Felder in jedem String werden miteinander verglichen.
  • Alphabetische Abschnitte werden lexikographisch verglichen und die numerischen Abschnitte werden numerisch verglichen.
  • Im Falle einer Fehlanpassung, bei der ein Feld numerisch ist und man alphabetisch ist, wird das numerische Feld immer größer (neuer).
  • In dem Fall, wo ein String aus Feldern läuft, wird der andere immer größer (neuer).

Siehe lib/rpmvercmp.c in der RPM-Quelle für die blutigen Details.

Hier ist ein Arbeitsprogramm auf der Basis von rpmdev-vercmp aus dem Paket rpmdevtools . Du brauchst nichts Besonderes zu installieren, aber yum (was das rpmUtils.miscutils python-Modul zur Verfügung stellt), damit es funktioniert.

Der Vorteil gegenüber den anderen Antworten ist, dass Sie nicht brauchen, um etwas zu analysieren, füttern Sie es einfach RPM Name-Version Strings wie:

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

Exit status 11 bedeutet, dass der erste neuer ist, 12 bedeutet, der zweite ist neuer.

 #!/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) 

Basierend auf Owen S's exzellenter Antwort, stelle ich ein Snippet, das die System-RPM-Bindungen verwendet, wenn verfügbar, aber fällt zurück auf eine Regex-basierte Emulation anders:

 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) 

Beachten Sie, dass ich dies nicht ausgiebig für die Konsistenz mit der C-Level-Implementierung getestet habe – ich benutze es nur als Fallback-Implementierung, die zumindest gut genug ist, um die Test-Suite von Anitya in Umgebungen zu übergeben, in denen System-RPM-Bindungen nicht verfügbar sind.

Eine viel einfachere regex ist /^(.+)-(.+)-(.+).(.+).rpm$/

Ich bin mir keine Beschränkungen für den Paketnamen (erstes Capture) bekannt. Die einzigen Einschränkungen für Version und Freigabe sind, dass sie nicht '-' enthalten. Es gibt keine Notwendigkeit, dies zu kodieren, da die Ungeklärteen '' 's diese Felder trennen, also wenn man ein' – 'hat, würde es gespalten werden und nicht ein einziges Feild sein, ergo die resultierende Aufnahme würde nicht ein' – ' . Nur die erste Erfassung, der Name, enthält irgendwelche '-', weil sie alle Fremden verbraucht '-' zuerst.

Dann gibt es die Architektur, die diese Regex keine Beschränkung für den Architekturnamen annimmt, außer dass sie kein '.' Enthält.

Die Erfassungsergebnisse sind [Name, Version, Freigabe, Bogen]

Caveats von Owens Antwort darauf, sich auf den rpm Namen allein zu verlassen, gilt immer noch.

RPM hat Pythonbindungen, mit denen Sie rpmUtils.miscutils.compareEVR verwenden können. Das erste und das dritte Argument des Tupels sind der Paketname und die Verpackungsversion. Die Mitte ist die Version. Im folgenden Beispiel versuche ich herauszufinden, wo 3.7.4a sortiert wird.

 [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 
  • Setup.py Beispiele?
  • Python ist die beste Programmiersprache der Welt.