将RPM名称parsing为其组件

有一个名称parsing工具是官方RPM工具包的一部分吗?

我有一个文件名列表。 每个都是RPM包的文件名。 我没有真正的软件包,只有文件名。 对于每个我需要提取包名称和版本($ NAME和$ VERSION)。 我需要这个的原因是我正在写一个脚本,然后确保“yum install $ VERSION”安装$ VERSION。 这是构build包并validation它们已正确上载的系统的一部分。

文件名列表如下所示:

$ 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)如果没有,是否有官方的正则expression式我可以使用。 (3)什么是正则expression式的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 

您可以使用的variables的完整列表可以通过以下方式获得:

 rpm --querytags 

请注意,在RELEASE的情况下,像84.el6这样的输出是正常的和预期的,因为这实际上是RPM包或版本化的版本。

我已经被告知官方的方式来做我正在寻找的是在Python中:

 from rpmUtils.miscutils import splitFilename (n, v, r, e, a) = splitFilename(filename) 

我已经写了一个简短的Python程序来完成我所需要的。 我将提供脚本到rpmdev项目列入。

我制定了适合所有数据的正则expression式,能够testing它们。 我不得不使用贪婪和非贪婪的比赛的混合物。 这就是说,这里是我的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项目的正则expression式。 我上面发明的那个现在必须做。

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 

只需再次拆分连字符来隔离你需要的部分。 显然,清理拱和rpm文件扩展名string,这是一个给定的。 只是给你一个关于如何在bash中接近的想法。

如前所述,从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') 

所以要注意 ,这不是rpm的正确细节,例如1.fedora其实1.fc10 rpm中的1.fc10

如果您熟悉正则expression式和/或Perl,那很简单。

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

或单独的正则expression式:

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

如果你分裂它是:

  • 除了连字符之外,至less有一个字符: [^\-]+ (因为连字符在字符组中有特殊含义而被转义)
  • 在第一个连字符(而不是最后一个)之后停止匹配: [^\-]+?
  • 将其添加到捕获组: ([^\-]+?)
  • 然后连字符: ([^\-]+?)-
  • 那么在另一个捕获组中的其他任何东西(但尾随.rpm ): ([^\-]+?)-(.*).rpm$ (美元的意思是“行尾”)
  • m#([^\-]+?)-(.*).rpm$#

完成! 只需在variables$1$2获取两个部分

评论第一个单行:

我在一个有许多rpm文件的目录中,因此是ls

perl -p相当于;

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

这解释了我必须在$_放置一个空string,以避免在我提取并自定义打印之后,perl打印回行。 请注意,我可以使用replace来使这个小小的“黑客”变得有效。

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

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

也就是说:反转每一行,使用斜杠剪切第一部分( emanelif ),然后用连字符剪切除前两部分(即留下ESAELER,包括emanelif eth fo tserNOISREV ),然后逆转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 $ 

(1)阅读其他部分的练习。