将RPM名称解析为其组件


19

官方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等效项是什么?

提前致谢!


您能否说明您从何处获得输入以及所需的格式。
user9517支持GoFundMonica13年

Answers:


25

您无需执行任何操作;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

请注意,在的情况下RELEASE84.el6通常会输出预期的输出,因为这实际上是RPM软件包在由发行版打包或为发行版打包时的版本控制方式。


2
仅适用于已安装的软件包。我想操纵文件名。 $ 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 2013年

@TomOnTime等一下...所以您不在乎包装中的实际内容是什么?
迈克尔·汉普顿

4
我希望我早点知道。RPM工具仅处理软件包中的内容。文件名是完全无关的(这个答案对您不起作用)。
迈克尔·汉普顿

1
进行有趣的解析,例如:libopenssl0_9_8-32bit-0.9.8j-0.26.1_0.50.1.x86_64.delta.rpm
MikeyB

5
@TomOnTime-“仅适用于已安装的软件包”否-在第三个示例中,您错过了-p选项:rpm --queryformat“%{NAME}%{VERSION}%{RELEASE}%{ARCH}” -qp文件.rpm
Sam Elstob

14

有人告诉我要做的正式方法是使用Python:

from rpmUtils.miscutils import splitFilename

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

我写了一个简短的Python程序来满足我的需要。我将脚本提供给rpmdev项目以包含在内。


1
Debian软件包的命名规则是如此简单明了-我不知道rpm世界如何陷入如此混乱。请您将脚本粘贴到这里的答案中吗?
保罗·黑德利

3

我制定了适合所有我可以测试它们的数据的正则表达式。我不得不混合使用贪婪和非贪婪的比赛。也就是说,这是我的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";
}

蟒蛇:

#! /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项目的正则表达式。我上面发明的那个现在必须要做。


这与我的解决方案最相似(但请避免使用,.*除非您确实要匹配任何内容)。很高兴看到您自己找到了!
mveroone 2013年

2
文件名令我震惊,因为它是获取此信息的一种不好的方式。它可能适用于特定的一组供应商提供的RPM(因此,只要您的供应商将第三方内容标准化,并且从不更改其命名格式,您就可以了),但是我看到了很多以创意命名的RPM文件。几秒钟前我从Adobe手中抢到的Acrobat Reader是AdbeRdr9.5.5-1_i486linux_enu.rpm),它破坏了上面的正则表达式解析。
voretaq13年

真正。但是Adbe不适用于任何解决方案,因为它违反了yum文件名标准。(从技术上讲,问题应该是关于yum文件名,而不是RPM文件名)。
TomOnTime

1

RPM文件在极端情况下可以具有一些时髦的文件名,但是通常您可以在连字符上拆分NVR。捕获是NVR的N(名称)部分可能包含连字符和下划线,但是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中进行处理。


1

如前所述,使用rpm中的-q --queryformat选项,如果要在未安装的软件包上执行此操作,则可以使用该-p选项指定rpm ,如下所示:

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是转速。


我看到混乱了。RPM不仅未安装,而且在此计算机上也没有安装。我正在处理软件包和文件名的列表。这是为了管理回购库存。它没有实际的软件包。
TomOnTime 2013年

0

如果您熟悉正则表达式和/或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打印回该行。请注意,我本可以使用替代词来弥补这个小小的“ hack”。


这对于数百个RPM名称根本不起作用,例如module-init-tools-3.9-21.el6_4.x86_64.rpm
Nemo 2014年

0

恕我直言,最简单的shell方法是:

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

也就是说:反转每行,使用斜线仅切掉第一部分(emanelif),然后使用连字符切掉除前两部分以外的所有部分(即,留下ESAELER(包括emanelif eth fotserNOISREV))并反转enil

使用示例文件:

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

要阅读其他内容,请阅读cut(1)


0

您可以利用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}"

即使未安装该软件包,它也会给出结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.