Analyser un nom RPM dans ses composants

19

Existe-t-il un outil d'analyse de nom qui fait partie du package d'outils RPM officiel?

J'ai une liste de noms de fichiers. Chacun est le nom de fichier d'un package RPM. Je n'ai pas les paquets réels, juste les noms de fichiers. Pour chacun, j'ai besoin d'extraire le nom et la version du package ($ NAME et $ VERSION). La raison pour laquelle j'en ai besoin est que j'écris un script qui s'assure ensuite que "yum install $ VERSION" installe $ VERSION. Cela fait partie d'un système qui construit des packages et vérifie qu'ils sont correctement téléchargés.

La liste des noms de fichiers ressemble à:

$ cat /tmp/packages.txt
/home/builder/packages/testing-dev/CentOS/6/x86_64/emacs-mercurial-2.8-3.el6.x86_64.rpm
/home/builder/packages/testing-dev/CentOS/6/x86_64/emacs-mercurial-el-2.8-3.el6.x86_64.rpm
/home/builder/packages/testing-dev/CentOS/6/x86_64/mercurial-2.8-3.el6.x86_64.rpm
/home/builder/packages/testing-dev/CentOS/6/x86_64/mercurial-hgk-2.8-3.el6.x86_64.rpm
/home/builder/packages/testing-dev/CentOS/6/x86_64/python-redis-2.8.0-2.el6.noarch.rpm
/home/builder/packages/testing-dev/CentOS/6/x86_64/redis-2.6.16-1.el6.1.x86_64.rpm
/home/builder/packages/testing-dev/CentOS/6/x86_64/sei_dnsmaster-1.0-99.el6.x86_64.rpm

J'ai trouvé le code suivant qui est une fonction BASH qui fait la tâche:

function parse_rpm() { RPM=$1;B=${RPM##*/};B=${B%.rpm};A=${B##*.};B=${B%.*};R=${B##*-};B=${B%-*};V=${B##*-};B=${B%-*};N=$B;echo "$N $V $R $A"; }

for i in $(</tmp/packages.txt) ; do
    parse_rpm $i
done

Ça marche. La plupart. Il y a quelques exceptions:

$ parse_rpm CentOS/6/x86_64/sei_dnsmaster-1.0-99.el6.x86_64.rpm
sei_dnsmaster 1.0 99.el6 x86_64

Notez qu'il n'a pas obtenu la version correctement (elle devrait être 1.0-99)

Je me demande (1) s'il y a un outil dans le paquetage rpmdev qui le fait correctement. (2) Sinon, y a-t-il une expression officielle que je pourrais utiliser. (3) Quel est l'équivalent python de cette expression régulière?

Merci d'avance!

TomOnTime
la source
Pouvez-vous clarifier d'où vous obtenez votre contribution et le format qu'elle prend, s'il vous plaît.
user9517 pris en chargeGoFundMonica

Réponses:

25

Vous n'avez pas besoin de faire quoi que ce soit; RPM a un argument de format de requête qui vous permettra de spécifier exactement les données que vous souhaitez recevoir. Il sortira même sans fin de ligne si vous ne les spécifiez pas.

Par exemple:

rpm --queryformat "%{NAME} %{VERSION} %{RELEASE} %{ARCH}" -q coreutils
rpm --queryformat "The version of %{NAME} is %{VERSION}\n" -q coreutils

rpm --queryformat "%{NAME} %{VERSION} %{RELEASE} %{ARCH}" -qp file.rpm

La liste complète des variables que vous pouvez utiliser peut être obtenue avec:

rpm --querytags

Notez que dans le cas de RELEASE, la sortie like 84.el6est normale et attendue, car c'est en fait la façon dont les packages RPM sont versionnés lorsqu'ils sont empaquetés par ou pour une distribution.

Michael Hampton
la source
2
Cela ne fonctionne qu'avec les packages installés. Je veux manipuler les noms de fichiers. $ rpm --queryformat "%{NAME} %{VERSION} %{RELEASE} %{ARCH}" -q CentOS/6/x86_64/sei_dnsmaster-1.0-84.el6.x86_64.rpm package CentOS/6/x86_64/sei_dnsmaster-1.0-84.el6.x86_64.rpm is not installed
TomOnTime
@TomOnTime Attendez une minute ... Vous ne vous souciez donc pas du contenu du package?
Michael Hampton
4
J'aurais aimé le savoir plus tôt. Les outils RPM ne traitent que le contenu du package; le nom de fichier est complètement hors de propos (et cette réponse ne fonctionnera pas pour vous).
Michael Hampton
1
Amusez-vous à analyser, par exemple:libopenssl0_9_8-32bit-0.9.8j-0.26.1_0.50.1.x86_64.delta.rpm
MikeyB
5
@TomOnTime - "Cela ne fonctionne qu'avec les packages installés" Pas vrai - vous avez manqué l'option -p dans le troisième exemple: rpm --queryformat "% {NAME}% {VERSION}% {RELEASE}% {ARCH}" -qp file .rpm
Sam Elstob
14

On m'a dit que la manière officielle de faire ce que je cherchais est en Python:

from rpmUtils.miscutils import splitFilename

(n, v, r, e, a) = splitFilename(filename)

J'ai écrit un petit programme Python qui fait ce dont j'ai besoin. Je proposerai le script au projet rpmdev pour inclusion.

TomOnTime
la source
1
Les règles de nommage des paquets Debian sont si simples et très simples - je ne sais pas comment le monde rpm s'est retrouvé dans un tel bordel. S'il vous plaît pourriez-vous coller votre script dans la réponse ici?
Paul Hedderly
3

J'ai élaboré des expressions régulières qui correspondent à toutes les données avec lesquelles j'ai pu les tester. J'ai dû utiliser un mélange de matchs gourmands et non gourmands. Cela dit, voici mes versions perl et python:

Perl:

#! /usr/bin/perl

foreach (@ARGV) {
    ($path, $name, $version, $release, $platform,
      @junk) = m#(.*/)*(.*)-(.*)-(.*?)\.(.*)(\.rpm)#;
    $verrel = $version . '-' . $release;

    print join("\t", $path, $name, $verrel, $version, $rev, $platform), "\n";
}

Python:

#! /usr/bin/python

import sys
import re

for x in sys.argv[1:]:
    m = re.search(r'(.*/)*(.*)-(.*)-(.*?)\.(.*)(\.rpm)', x)
    if m:
        (path, name, version, release, platform, _) = m.groups()
        path = path or ''
        verrel = version + '-' + release
        print "\t".join([path, name, verrel, version, release, platform])
    else:
        sys.stderr.write('ERROR: Invalid name: %s\n' % x)
        sys.exit(1)

Je préfère avoir une expression régulière qui provient du projet RPM. Celui que j'ai inventé ci-dessus devra faire pour l'instant.

TomOnTime
la source
C'est principalement similaire à ma solution (mais évitez à .*moins que vous ne vouliez VRAIMENT correspondre à quoi que ce soit). C'est bien de voir que vous l'avez trouvé par vous-même!
mveroone
2
Le nom de fichier me semble être un mauvais moyen d'obtenir ces informations. Cela peut fonctionner pour un ensemble spécifique de RPM fournis par le fournisseur (vous pouvez donc être d'accord tant que votre fournisseur standardise les éléments tiers et ne change jamais leur format de dénomination), mais j'ai vu de nombreux fichiers RPM nommés de manière créative. Le Acrobat Reader que j'ai récupéré d'Adobe il y a quelques secondes est AdbeRdr9.5.5-1_i486linux_enu.rpm) qui rompt l'analyse syntaxique ci-dessus.
voretaq7
Vrai. Mais Adbe ne fonctionnerait pour aucune solution car elle enfreint la norme de nom de fichier yum. (Techniquement, la question devrait concerner les noms de fichiers miam, pas les noms de fichiers RPM).
TomOnTime
1

Les fichiers RPM peuvent avoir des noms de fichiers géniaux dans les cas extrêmes, mais généralement vous pouvez diviser le NVR sur les tirets. Le hic est que la partie N (nom) du NVR peut contenir des traits d'union et des traits de soulignement, mais les V (version) et R (version) sont garantis de ne pas avoir de tirets étrangers. Vous pouvez donc commencer par rogner la partie VR pour dériver un nom.

$ RPM=/home/builder/packages/testing-dev/CentOS/6/x86_64/emacs-mercurial-2.8-3.el6.x86_64.rpm
$ echo ${RPM%-*-*}
/home/builder/packages/testing-dev/CentOS/6/x86_64/emacs-mercurial

En s'appuyant sur cela, vous pouvez isoler la partie version et version.

echo ${RPM#${RPM%-*-*}-*}
2.8-3.el6.x86_64.rpm

Il suffit de diviser à nouveau le tiret pour isoler la pièce dont vous avez besoin. Et évidemment, nettoyez les chaînes d'extension de fichier arch et rpm, ce qui est un fait. Je vous donne juste une idée de la façon dont cela pourrait être abordé en bash.

masta
la source
1

Utilisez les options -q --queryformat de rpm comme indiqué précédemment, si vous voulez le faire sur un paquet non installé, vous pouvez spécifier le rpm avec l' -poption, comme ceci:

rpm -q -p ./Downloads/polysh-0.4-1.noarch.rpm --queryformat "%{NAME} %{VERSION} %{RELEASE} %{ARCH}\n"
polysh 0.4 1 noarch

par exemple

$ ls ./Downloads/*.rpm
./Downloads/adobe-release-x86_64-1.0-1.noarch.rpm
./Downloads/nautilus-dropbox-1.6.0-1.fedora.x86_64.rpm
./Downloads/playonlinux-yum-4-1.noarch.rpm
./Downloads/skype-4.2.0.11-fedora.i586.rpm
./Downloads/dbview-1.0.4-2.1.x86_64.rpm
./Downloads/openmotif22-libs-2.2.4-192.1.3.x86_64.rpm
./Downloads/polysh-0.4-1.noarch.rpm

Donne moi

adobe-release-x86_64 1.0 1 noarch
dbview 1.0.4 2.1 x86_64
nautilus-dropbox 1.6.0 1.fc10 x86_64
openmotif22-libs 2.2.4 192.1.3 x86_64
playonlinux-yum 4 1 noarch
polysh 0.4 1 noarch
skype 4.2.0.11 fc16 i586

il suffit donc de diviser le nom de fichier!

for filename in """<paste list here>""".split():
    print splitFilename(filename)

('./Downloads/adobe-release-x86_64', '1.0', '1', '', 'noarch')
('./Downloads/nautilus-dropbox', '1.6.0', '1.fedora', '', 'x86_64')
('./Downloads/playonlinux-yum', '4', '1', '', 'noarch')
('./Downloads/skype', '4.2.0.11', 'fedora', '', 'i586')
('./Downloads/dbview', '1.0.4', '2.1', '', 'x86_64')
('./Downloads/openmotif22-libs', '2.2.4', '192.1.3', '', 'x86_64')
('./Downloads/polysh', '0.4', '1', '', 'noarch')

alors faites attention , ce ne sont pas les détails corrects du régime, par exemple, il 1.fedoraest en fait 1.fc10dans le régime.

Jens Timmerman
la source
Je vois la confusion. Non seulement le RPM n'est pas installé, mais je ne l'ai pas sur cette machine. Je traite des listes de packages et de noms de fichiers. C'est pour quelque chose qui gère les inventaires repo; il n'a pas les packages réels.
TomOnTime
0

Si vous connaissez les expressions régulières et / ou Perl, c'est assez simple.

 ls | head | perl -p -e 'm#([^\-]+?)-(.*).rpm$#; print "$1 $2\n";$_=""' 

ou le regex seul:

m#([^\-]+?)-(.*).rpm$#

Si vous le divisez, c'est:

  • tout sauf un [^\-]+trait d' union, au moins un caractère: (échappé car le trait d'union a une signification particulière dans les groupes de caractères)
  • arrêter le match après le premier tiret (et non le dernier): [^\-]+?
  • ajoutez ceci à un groupe de capture: ([^\-]+?)
  • Puis un tiret: ([^\-]+?)-
  • puis toute autre chose dans un autre groupe de capture (mais la fin .rpm): ([^\-]+?)-(.*).rpm$ (le dollar signifie "fin de ligne")
  • joindre que dans un format de correspondance pratique: m#([^\-]+?)-(.*).rpm$#

Terminé ! Obtenez simplement les deux parties dans les variables $1et$2

Commentaire sur le premier one-liner:

J'étais dans un répertoire contenant de nombreux fichiers rpm, d'où le ls.

perl -p est équivalent à ;

perl -e 'while(<STDIN>){ chomp($_);  [YOUR CODE HERE] ; print($_); }' 

Ce qui explique que j'ai dû mettre une chaîne nulle $_pour éviter que perl n'imprime la ligne après l'avoir extraite et imprimée sur mesure. Notez que j'aurais pu utiliser des substitutions pour éviter ce petit «hack».

mveroone
la source
Cela ne fonctionne pas du tout sur des centaines de noms RPM, par exemple module-init-tools-3.9-21.el6_4.x86_64.rpm.
Nemo
0

À mon humble avis, la manière la plus simple de shell est:

ls | rev | cut -d/ -f1 | cut -d- -f3- | rev

C'est-à-dire: inverser chaque ligne, en utilisant la coupe oblique uniquement la première partie ( emanelif ), puis en utilisant la coupure de trait d'union, sauf les deux premières parties (c'est-à-dire laisser derrière ESAELER, y compris emanelif eth fo tser et NOISREV ) et inverser l' énil en arrière.

Avec votre fichier d'exemple:

$ cat /tmp/packages.txt | rev | cut -d/ -f1 | cut -d- -f3- | rev
emacs-mercurial
emacs-mercurial-el
mercurial
mercurial-hgk
python-redis
redis
sei_dnsmaster
$

Pour obtenir d'autres pièces, il faut exercer la lecture de la coupe (1) .

Alois Mahdal
la source
0

Vous pouvez utiliser dnf info. Voici un exemple de script Bash pour obtenir des valeurs et définir en tant que variable:

function dnfinfo() {
   dnf info "$(echo "${1}" | sed 's/\.rpm$//g')"
}

function splitname() {
   eval $(
     dnfinfo "${1}" | \
     grep "^Arch\|^Name\|^Release\|^Version" | \
     sort | \
     awk -F": " {'print "\""$2"\""'} | \
     tr "\n" " " | \
     awk {'print "xarch="$1"~xname="$2"~xrel="$3"~xver="$4'} | \
     tr "~" "\n"
   )
}

splitname "tcpdump-4.9.2-5.el8.x86_64.rpm"
echo "${xname} ${xver} ${xrel} ${xarch}"

Cela donnera un résultat même si le paquet n'est pas installé.

uboreas
la source