Есть ли инструмент разбора имен, который входит в официальный пакет инструментов 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}"
Это даст результат, даже если пакет не установлен.