Analisar um nome de RPM em seus componentes

19

Existe uma ferramenta de análise de nome que faz parte do pacote oficial de ferramentas RPM?

Eu tenho uma lista de nomes de arquivos. Cada um é o nome do arquivo de um pacote RPM. Eu não tenho os pacotes reais, apenas os nomes dos arquivos. Para cada um, preciso extrair o nome e a versão do pacote ($ NAME e $ VERSION). A razão pela qual eu preciso disso é que estou escrevendo um script que garante que "yum install $ VERSION" instale $ VERSION. Isso faz parte de um sistema que cria pacotes e verifica se eles foram enviados corretamente.

A lista de nomes de arquivos é semelhante a:

$ 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

Encontrei o seguinte código, que é uma função BASH que executa a tarefa:

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. Na maioria das vezes. Existem algumas exceções:

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

Observe que ela não obteve a versão corretamente (deve ser 1.0-99)

Gostaria de saber (1) se existe uma ferramenta no pacote rpmdev que faz isso corretamente. (2) Caso contrário, existe um regex oficial que eu poderia usar. (3) Qual é o equivalente python desse regex?

Desde já, obrigado!

TomOnTime
fonte
Você pode esclarecer de onde está obtendo sua opinião e o formato necessário?
user9517 suporta GoFundMonica

Respostas:

25

Você não precisa fazer nada disso; O RPM possui um argumento de formato de consulta que permite especificar exatamente os dados que você deseja receber. Ele até produzirá sem terminações de linha se você não as especificar.

Por exemplo:

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

A lista completa de variáveis ​​que você pode usar pode ser obtida com:

rpm --querytags

Observe que, no caso de RELEASE, saída como 84.el6é normal e esperada, pois é assim que os pacotes RPM são versionados quando empacotados por ou para uma distribuição.

Michael Hampton
fonte
2
Isso funciona apenas com pacotes instalados. Eu quero manipular nomes de arquivos. $ 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
@ Tomom Time Espere um minuto ... Então você não se importa com o que realmente está no pacote?
Michael Hampton
4
Eu gostaria de saber disso mais cedo. As ferramentas RPM lidam apenas com o conteúdo da embalagem; o nome do arquivo é completamente irrelevante (e esta resposta não funcionará para você).
Michael Hampton
11
Divirta-se analisando, por exemplo:libopenssl0_9_8-32bit-0.9.8j-0.26.1_0.50.1.x86_64.delta.rpm
MikeyB 26/11
5
@TomOnTime - "Isso funciona apenas com pacotes instalados" Não é verdade - você perdeu a opção -p no terceiro exemplo: rpm --queryformat "% {NAME}% {VERSION}% {RELEASE}% {ARCH}" -qp .rpm
Sam Elstob
14

Foi-me dito que a maneira oficial de fazer o que estou procurando é no Python:

from rpmUtils.miscutils import splitFilename

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

Eu escrevi um pequeno programa Python que faz o que eu preciso. Vou oferecer o script para o projeto rpmdev para inclusão.

TomOnTime
fonte
11
As regras de nomenclatura dos pacotes Debian são muito simples e diretas - eu não sei como o mundo do rpm acabou com essa confusão. Por favor, você pode colar seu script na resposta aqui?
Paul Hedderly
3

Elaborei expressões regulares que se encaixavam em todos os dados com os quais eu era capaz de testá-las. Eu tive que usar uma mistura de combinações gananciosas e não gananciosas. Dito isto, aqui estão minhas versões perl e 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ão:

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

Prefiro ter uma regex que vem do projeto RPM. O que eu inventei acima terá que fazer por enquanto.

TomOnTime
fonte
Isso é mais parecido com a minha solução (mas evite, a .*menos que você realmente queira corresponder a algo). É bom ver que você encontrou sozinho!
mveroone
2
O nome do arquivo me parece uma maneira ruim de obter essas informações. Pode funcionar para um conjunto específico de RPMs fornecidos pelo fornecedor (para que você possa ficar bem desde que seu fornecedor padronize coisas de terceiros e nunca mude o formato de nomes), mas eu já vi muitos arquivos RPM com nomes criativos. O Acrobat Reader que peguei da Adobe há alguns segundos é AdbeRdr9.5.5-1_i486linux_enu.rpm) que interrompe a análise de expressões regulares acima.
voretaq7
Verdade. Mas o Adbe não funcionaria em nenhuma solução, pois quebra o padrão do nome de arquivo yum. (Tecnicamente, a pergunta deve ser sobre nomes de arquivos yum, não nomes de arquivos RPM).
TomOnTime
1

Os arquivos RPM podem ter nomes de arquivos descolados em casos extremos, mas geralmente você pode dividir o NVR nos hífens. O problema é que a parte N (nome) do NVR pode conter hífens e sublinhados, mas é garantido que o V (versão) e o R (release) não possuem hífens estranhos. Assim, você pode começar aparando a parte VR para obter um Nome.

$ 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

Com base nisso, você pode isolar a parte Versão e Liberação.

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

Basta dividir o hífen novamente para isolar a parte que você precisa. E, obviamente, limpe as strings de extensão de arquivo arch e rpm, o que é um dado. Apenas fornecendo uma idéia de como isso pode ser abordado no bash.

masta
fonte
1

Use as opções -q --queryformat do rpm, como dito anteriormente, se você quiser fazer isso em um pacote não instalado, poderá especificar o rpm com a -popção, assim:

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

por exemplo

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

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

apenas dividir o nome do arquivo está errado!

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

então preste atenção , este não é o detalhe correto da rotação, por exemplo, 1.fedoraestá realmente 1.fc10na rotação.

Jens Timmerman
fonte
Eu vejo a confusão. Não apenas o RPM não está instalado, como também não o tenho nesta máquina. Estou processando listas de pacotes e nomes de arquivos. Isso é para algo que gerencia inventários de recompra; não possui os pacotes reais.
precisa saber é o seguinte
0

Se você estiver familiarizado com expressões regulares e / ou Perl, isso é bastante fácil.

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

ou apenas a regex:

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

Se você dividir, é isso:

  • qualquer coisa, exceto um hífen, pelo menos um caractere: [^\-]+(escapou porque o hífen tem um significado especial nos grupos de caracteres)
  • pare a partida após o primeiro hífen (e não o último): [^\-]+?
  • adicione isso a um grupo de captura: ([^\-]+?)
  • Então um hífen: ([^\-]+?)-
  • depois, qualquer outra coisa em outro grupo de captura (exceto o final .rpm): ([^\-]+?)-(.*).rpm$ (o dólar significa "fim de linha")
  • coloque do que em um formato prático de correspondência: m#([^\-]+?)-(.*).rpm$#

Feito ! Apenas pegue as duas partes nas variáveis $1e$2

Comente o primeiro one-liner:

Eu estava em um diretório com muitos arquivos rpm, daí o ls.

perl -p é equivalente a ;

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

O que explica que eu tive que inserir uma sequência nula $_para evitar a impressão perl na linha depois de extraí-la e imprimi-la personalizada. Note que eu poderia ter usado substituições para evitar esse pequeno 'hack'.

mveroone
fonte
Isso não funciona em centenas de nomes de RPM, por exemplo module-init-tools-3.9-21.el6_4.x86_64.rpm.
Nemo
0

IMHO a maneira mais simples de shell é:

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

Ou seja: reverter cada linha, usando a barra de corte apenas a primeira parte ( emanelif ), em seguida, usando hífen cortar tudo, mas as duas primeiras partes (isto é, deixar para trás ESAELER incluindo emanelif eth fo TSER e NOISREV ) e inverter o ENIL volta.

Com o seu arquivo de exemplo:

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

Obter outras peças é um exercício de leitura de corte (1) .

Alois Mahdal
fonte
0

Você pode utilizar dnf info. Aqui está um exemplo de script Bash para obter valores e definir como uma variável:

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}"

Isso dará um resultado mesmo se o pacote não estiver instalado.

uboreas
fonte