Назад | Перейти на главную страницу

Разберите имя RPM на его компоненты

Есть ли инструмент разбора имен, который входит в официальный пакет инструментов RPM?

У меня есть список имен файлов. Каждое из них - имя файла RPM-пакета. У меня нет фактических пакетов, только имена файлов. Для каждого мне нужно извлечь имя и версию пакета ($ NAME и $ VERSION). Причина, по которой мне это нужно, заключается в том, что я пишу сценарий, который затем проверяет, что "yum install $ VERSION" устанавливает $ VERSION. Это часть системы, которая собирает пакеты и проверяет, правильно ли они загружены.

Список имен файлов выглядит так:

$ 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

я нашел следующий код которая является функцией BASH, которая выполняет задачу:

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

Оно работает. В основном. Есть некоторые исключения:

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

Обратите внимание, что он неправильно получил версию (она должна быть 1.0-99)

Мне интересно (1), есть ли в пакете rpmdev инструмент, который делает это правильно. (2) Если нет, могу ли я использовать официальное регулярное выражение. (3) Что такое Python-эквивалент этого регулярного выражения?

Заранее спасибо!

Вам не нужно ничего этого делать; RPM имеет аргумент формата запроса, который позволит вам точно указать данные, которые вы хотите получить. Он будет выводиться даже без окончания строки, если вы их не укажете.

Например:

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

Полный список переменных, которые вы можете использовать, можно получить с помощью:

rpm --querytags

Обратите внимание, что в случае RELEASE, вывод как 84.el6 Это нормально и ожидаемо, поскольку именно так формируются версии пакетов RPM, когда они упакованы дистрибутивом или для него.

Мне сказали, что официальный способ делать то, что я ищу, - это Python:

from rpmUtils.miscutils import splitFilename

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

Я написал небольшую программу на Python, которая делает то, что мне нужно. Предложу скрипт в проект rpmdev для включения.

Я разработал регулярные выражения, подходящие ко всем данным, с которыми я смог их протестировать. Мне пришлось использовать смесь жадных и нежадных спичек. Тем не менее, вот мои версии на perl и 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)

Я бы предпочел регулярное выражение из проекта RPM. То, что я изобрел выше, пока придется сделать.

В крайних случаях файлы Rpm могут иметь необычные имена, но обычно вы можете разделить NVR по дефисам. Загвоздка в том, что часть N (имя) NVR может содержать дефисы и подчеркивания, но гарантировано, что V (версия) и R (выпуск) не будут иметь лишних дефисов. Итак, вы можете начать с обрезки части VR, чтобы получить имя.

$ 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

Опираясь на это, вы можете изолировать части версии и выпуска.

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

Просто разделите дефис еще раз, чтобы выделить нужную часть. И, очевидно, очистите строки расширений файлов arch и rpm, что является заданным. Просто даю вам представление о том, как это можно сделать в bash.

Используйте параметры -q --queryformat из rpm, как было сказано ранее, если вы хотите сделать это для неустановленного пакета, вы можете указать rpm с помощью -p вариант, например:

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

например

$ 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

дает мне

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

так что просто разбивать имя файла неправильно!

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

так обращать внимание, это неверные данные об оборотах, например 1.fedora на самом деле 1.fc10 в об / мин.

Если вы знакомы с регулярными выражениями и / или Perl, это довольно просто.

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

или только регулярное выражение:

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

Если вы его разделите, это:

  • что угодно, кроме дефиса, хотя бы один символ: [^\-]+ (экранирован, потому что дефис имеет особое значение в группах символов)
  • остановить соответствие после первого дефиса (а не последнего): [^\-]+?
  • добавьте это в группу захвата: ([^\-]+?)
  • Затем дефис: ([^\-]+?)-
  • затем что-нибудь еще в другой группе захвата (кроме конечных .rpm): ([^\-]+?)-(.*).rpm$ (доллар означает «конец строки»)
  • заключите, чем в практический формат соответствия: m#([^\-]+?)-(.*).rpm$#

Готово ! Просто поместите обе части в переменные $1и $2

Комментарий к первому однострочному письму:

Я находился в каталоге со многими файлами rpm, поэтому ls.

perl -p эквивалентно ;

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

Это объясняет, что мне пришлось поместить нулевую строку в $_ чтобы Perl не печатал строку обратно после того, как я ее извлек и напечатал. Обратите внимание, что я мог использовать замены, чтобы избежать этого небольшого «взлома».

ИМХО, самый простой способ оболочки:

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

То есть: переверните каждую строку, используя косую черту, вырежьте только первую часть (Emanelif), затем с помощью дефиса вырезать все, кроме первых двух частей (т.е. оставить ESAELER включая Эманелиф Эт Фоцер и NOISREV) и переверните Энил назад.

В вашем примере файла:

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

Чтобы получить другие части, выполните упражнения по чтению вырезать (1).

Вы можете использовать dnf info. Вот пример сценария Bash для получения значений и их установки в качестве переменной:

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

Это даст результат, даже если пакет не установлен.