Analizar un nombre RPM en sus componentes

19

¿Existe una herramienta de análisis de nombres que sea parte del paquete oficial de herramientas RPM?

Tengo una lista de nombres de archivo. Cada uno es el nombre de archivo de un paquete RPM. No tengo los paquetes reales, solo los nombres de archivo. Para cada uno, necesito extraer el nombre y la versión del paquete ($ NAME y $ VERSION). La razón por la que necesito esto es porque estoy escribiendo un script que luego se asegura de que "yum install $ VERSION" instale $ VERSION. Esto es parte de un sistema que crea paquetes y verifica que estén cargados correctamente.

La lista de nombres de archivo se ve así:

$ 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

Encontré el siguiente código, que es una función BASH que hace la tarea:

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

Funciona. Principalmente. Hay algunas excepciones:

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

Tenga en cuenta que no obtuvo la versión correctamente (debería ser 1.0-99)

Me pregunto (1) si hay una herramienta en el paquete rpmdev que hace esto correctamente. (2) Si no, ¿hay una expresión regular oficial que pueda usar? (3) ¿Cuál es el equivalente en python de esa expresión regular?

¡Gracias por adelantado!

TomOnTime
fuente
¿Puede aclarar de dónde obtiene su entrada y el formato que toma, por favor?
user9517 es compatible con GoFundMonica el

Respuestas:

25

No necesitas hacer nada de esto; RPM tiene un argumento de formato de consulta que le permitirá especificar exactamente los datos que desea recibir. Incluso saldrá sin terminaciones de línea si no las especifica.

Por ejemplo:

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 lista completa de variables que puede usar se puede obtener con:

rpm --querytags

Tenga en cuenta que, en el caso de RELEASE, la salida como 84.el6es normal y esperada, ya que así es como se versionan los paquetes RPM cuando se empaquetan por o para una distribución.

Michael Hampton
fuente
2
Eso solo funciona con paquetes instalados. Quiero manipular nombres de archivo. $ 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 Espera un minuto ... ¿Entonces no te importa lo que hay realmente en el paquete?
Michael Hampton
44
Desearía haberlo sabido antes. Las herramientas RPM solo se ocupan del contenido del paquete; el nombre del archivo es completamente irrelevante (y esta respuesta no funcionará para usted).
Michael Hampton
1
Diviértete analizando, por ejemplo:libopenssl0_9_8-32bit-0.9.8j-0.26.1_0.50.1.x86_64.delta.rpm
MikeyB
55
@TomOnTime - "Eso solo funciona con paquetes instalados" No es cierto: se perdió la opción -p en el tercer ejemplo: rpm --queryformat "% {NAME}% {VERSION}% {RELEASE}% {ARCH}" -qp file .rpm
Sam Elstob
14

Me dijeron que la forma oficial de hacer lo que estoy buscando es en Python:

from rpmUtils.miscutils import splitFilename

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

He escrito un breve programa de Python que hace lo que necesito. Ofreceré el script al proyecto rpmdev para su inclusión.

TomOnTime
fuente
1
Las reglas de nomenclatura del paquete Debian son tan simples y directas que no sé cómo el mundo rpm ha terminado en tal desorden. ¿Podría pegar su script en la respuesta aquí?
Paul Hedderly
3

Trabajé expresiones regulares que se ajustan a todos los datos con los que pude probarlas. Tuve que usar una mezcla de partidos codiciosos y no codiciosos. Dicho esto, aquí están mis versiones de Perl y 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";
}

Pitón:

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

Prefiero tener una expresión regular que proviene del proyecto RPM. El que inventé arriba tendrá que hacer por ahora.

TomOnTime
fuente
Esto es principalmente similar a mi solución (pero evítelo a .*menos que REALMENTE quiera hacer coincidir algo). ¡Sin embargo, es bueno ver que lo encontraste solo!
mveroone
2
El nombre del archivo me parece una mala manera de obtener esta información. Puede funcionar para un conjunto específico de RPM proporcionados por el proveedor (por lo que puede estar bien siempre que su proveedor estandarice cosas de terceros y nunca cambie su formato de nomenclatura), pero he visto muchos archivos RPM con nombres creativos. El Acrobat Reader que tomé de Adobe hace unos segundos es AdbeRdr9.5.5-1_i486linux_enu.rpm) que rompe su análisis de expresiones regulares arriba.
voretaq7
Cierto. Pero Adbe no funcionaría para ninguna solución porque rompe el estándar de nombre de archivo yum. (Técnicamente, la pregunta debería ser sobre los nombres de archivo yum, no los nombres de archivo RPM).
TomOnTime
1

Los archivos Rpm pueden tener algunos nombres de archivos originales en casos extremos, pero generalmente puede dividir el NVR en los guiones. El problema es que la porción N (nombre) del NVR puede contener guiones y guiones bajos, pero se garantiza que la V (versión) y R (versión) no tienen guiones extraños. Por lo tanto, puede comenzar recortando la porción VR para derivar un Nombre.

$ 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

Partiendo de eso, puede aislar la porción Versión y Release.

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

Simplemente divida el guión nuevamente para aislar la parte que necesita. Y obviamente, limpie las cadenas de extensión de archivo de arco y rpm, lo cual es un hecho. Solo te doy una idea de cómo podría abordarse en bash.

masta
fuente
1

Use las opciones -q --queryformat de rpm como se dijo anteriormente, si desea hacer esto en un paquete no instalado, puede especificar las rpm con la -popción, de esta manera:

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

p.ej

$ 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

me da

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

¡así que dividir el nombre de archivo está mal!

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

así que preste atención , estos no son los detalles correctos de las rpm, por ejemplo, 1.fedoraestá realmente 1.fc10en las rpm.

Jens Timmerman
fuente
Veo la confusión No solo no está instalado el RPM, no lo tengo en esta máquina. Estoy procesando listas de paquetes y nombres de archivos. Esto es para algo que gestiona inventarios de repos; No tiene los paquetes reales.
TomOnTime
0

Si está familiarizado con las expresiones regulares y / o Perl, eso es bastante fácil.

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

o solo la expresión regular:

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

Si lo divide eso es:

  • cualquier cosa menos un guión, al menos un carácter: [^\-]+(escapó porque el guión tiene un significado especial en los grupos de caracteres)
  • detener el partido después del primer guión (y no el último): [^\-]+?
  • agregue esto a un grupo de captura: ([^\-]+?)
  • Luego un guión: ([^\-]+?)-
  • entonces cualquier otra cosa en otro grupo de captura (excepto el final .rpm): ([^\-]+?)-(.*).rpm$ (el dólar significa "fin de línea")
  • adjunte que en un práctico formato de correspondencia: m#([^\-]+?)-(.*).rpm$#

Hecho ! Simplemente obtenga ambas partes en las variables $1y$2

Comenta sobre el primer one-liner:

Estaba en un directorio con muchos archivos rpm, de ahí el ls.

perl -p es equivalente a ;

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

Lo que explica que tuve que poner una cadena nula $_para evitar que Perl volviera a imprimir la línea después de extraerla y personalizarla. Tenga en cuenta que podría haber usado sustituciones para evitar este pequeño 'truco'.

mveroone
fuente
Esto no funciona en absoluto en cientos de nombres RPM, por ejemplo module-init-tools-3.9-21.el6_4.x86_64.rpm.
Nemo
0

En mi humilde opinión, la forma de shell más simple es:

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

Es decir: invierta cada línea, usando una barra cortada solo en la primera parte ( emanelif ), luego use un guión cortado en todas las primeras dos partes (es decir, deje ESAELER incluido emanelif eth foser y NOISREV ) e invierta el enil back.

Con su archivo de ejemplo:

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

Para obtener otras partes es ejercicio en lectura corte (1) .

Alois Mahdal
fuente
0

Puedes utilizar dnf info. Aquí hay un ejemplo de script Bash para obtener valores y establecer como una 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}"

Dará un resultado incluso si el paquete no está instalado.

uboreas
fuente