RegEx 用于解析 Fedora / Red Hat 软件包的软件包名称、软件包版本(包括发行版)

RegEx for parsing package name, package version (including release) for Fedora / Red Hat packages

提问人:MaxU - stand with Ukraine 提问时间:10/8/2023 最后编辑:MaxU - stand with Ukraine 更新时间:10/9/2023 访问量:208

问:

我的目标是正确解析 Fedora 或 Red Hat 软件包的软件包名称和软件包版本(包括发行号),如下所示:

python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1.x86_64
python3.11-3.11.2-2.el9_2.2.aarch64
glibc-2.34-60.el9.aarch64
glib2-2.68.4-6.el9.aarch64
langpacks-core-font-en-3.0-16.el9.noarch
p11-kit-trust-0.24.1-2.el9.aarch64
tzdata-2023c-1.el9.noarch

预期结果:

pkg: python39   version: 3.9.16-1
pkg: python3.11 version: 3.11.2-2
pkg: glibc  version: 2.34-60
pkg: glib2  version: 2.68.4-6
pkg: langpacks-core-font-en version: 3.0-16
pkg: p11-kit-trust: version: 0.24.1-2
pkg: tzdata version: 2023c-1

这是我的尝试:

echo -e "python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1.x86_64\npython3.11-3.11.2-2.el9_2.2.aarch64\nglibc-2.34-60.el9.aarch64\nglib2-2.68.4-6.el9.aarch64\nlangpacks-core-font-en-3.0-16.el9.noarch\np11-kit-trust-0.24.1-2.el9.aarch64\ntzdata-2023c-1.el9.noarch" > pkgs.txt
cat pkgs.txt | sed -E 's/([^-]*)-([0-9]+(\.[0-9]+)*(-[0-9]+)?)([^0-9].*)?/pkg: \1\tversion: \2/'

我得到:

pkg: python39   version: 3.9.16-1
pkg: python3.11 version: 3.11.2-2
pkg: glibc  version: 2.34-60
pkg: glib2  version: 2.68.4-6
langpacks-core-font-pkg: en version: 3.0-16
p11-kit-pkg: trust  version: 0.24.1-2
pkg: tzdata version: 2023

请帮助我修复/改进正则表达式以正确解析包名称和包版本

更新我将在具有最少安装的操作系统包的容器中执行此命令(类似于 )。所以我不会安装和安装,只有一组最少的命令,如:、、、等。ubi-minimalperlpythoncatsedawkgrep

正则表达式 bash awk sed

评论

0赞 Léa Gris 10/8/2023
从 Fedora 或 RedHat 内置包管理器工具获取版本可能是更好的选择,而不是尝试解析不可靠的名称格式,例如 .请参见:stackoverflow.com/a/21496161/7939871rpmrpm -qp --queryformat '%{VERSION}' package.rpm
1赞 jqurious 10/8/2023
看起来是唯一“错误”的结果吗?您的模式只允许在破折号后使用数字: - 您可以减少限制,例如 - 但目前尚不清楚是否所有可能的软件包名称都遵循该确切的格式,因此建议查询软件包管理器。2023c-1(-[0-9]+)s/([^-]*)-([0-9][^-]*-[0-9]*).*/
1赞 tripleee 10/8/2023
你有没有注意到菲利普的回答已经提供了解决方案?这对你不起作用吗?sed
1赞 jqurious 10/9/2023
啊对了。是的,由于缺乏非贪婪支持,因此 SED 中的单一模式可能是不可能的。只是为了纠正我自己: - 这基本上等同于下面的答案。sed -E 's/\.[^0-9].*//; s/-([0-9].*)/ version: \1/; s/^/pkg: /'
2赞 pjh 10/9/2023
请参阅将 RPM 名称解析为其组件

答:

4赞 Philippe 10/8/2023 #1

如果你能使用 perl :

perl -pe 's/(.*?)-([0-9].*?)\.[^0-9].*/pkg: $1\tversion: $2/' << EOF
python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1.x86_64
python3.11-3.11.2-2.el9_2.2.aarch64
glibc-2.34-60.el9.aarch64
glib2-2.68.4-6.el9.aarch64
langpacks-core-font-en-3.0-16.el9.noarch
p11-kit-trust-0.24.1-2.el9.aarch64
tzdata-2023c-1.el9.noarch
EOF

sedversion,假设包名中没有@:

sed -E 's/\.[^0-9].*$// # Remove everything after version
        s/(-[0-9].*)/@\1/ # Insert @ before version
        s/([^@]+)@-(.*)/pkg: \1\tversion: \2/' << EOF
python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1.x86_64
python3.11-3.11.2-2.el9_2.2.aarch64
glibc-2.34-60.el9.aarch64
glib2-2.68.4-6.el9.aarch64
langpacks-core-font-en-3.0-16.el9.noarch
p11-kit-trust-0.24.1-2.el9.aarch64
tzdata-2023c-1.el9.noarch
EOF

评论

0赞 MaxU - stand with Ukraine 10/8/2023
谢谢你的回答!我想避免使用 perl,因为此命令将在基于 ubi-minimal Red Hat 映像的容器中执行,并安装最少数量的操作系统包。如果我需要额外安装 - 这将需要安装大量新的操作系统包。perl
0赞 pjh 10/20/2023
这两种解决方案通常都不起作用。尝试使用(真正的)Fedora 软件包。软件包名称为 ,版本是 ,发行版是 ,体系结构是 。(Java 软件包在名称中嵌入了主要版本,以允许同时安装多个版本。此包所需的输出为 。两种建议的解决方案都会产生 .java-17-openjdk-17.0.6.0.10-1.fc38.x86_64java-17-openjdk17.0.6.0.101.fc38x86_64pkg: java-17-openjdk version: 17.0.6.0.10-1pkg: java version: 17-openjdk-17.0.6.0.10-1
2赞 Léa Gris 10/8/2023 #2

使用 RedHat 或 Fedora 提供的包管理器返回名称和版本无疑比使用正则表达式解析包名更可靠。rpm

例:

#!/bin/sh
for pkg in
  python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1.x86_64 \
  python3.11-3.11.2-2.el9_2.2.aarch64 \
  glibc-2.34-60.el9.aarch64 \
  glib2-2.68.4-6.el9.aarch64 \
  langpacks-core-font-en-3.0-16.el9.noarch \
  p11-kit-trust-0.24.1-2.el9.aarch64 \
  tzdata-2023c-1.el9.noarch
do
  rpm -qp --queryformat 'pkg: %{NAME} version: %{VERSION}' "$pkg.rpm"
done

或者,如果具有所有文件名,则甚至不需要循环:.rpm

rpm -qp --queryformat 'pkg: %{NAME} version: %{VERSION}' \
  python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1.x86_64.rpm \
  python3.11-3.11.2-2.el9_2.2.aarch64.rpm \
  glibc-2.34-60.el9.aarch64.rpm \
  glib2-2.68.4-6.el9.aarch64.rpm \
  langpacks-core-font-en-3.0-16.el9.noarch.rpm \
  p11-kit-trust-0.24.1-2.el9.aarch64.rpm \
  tzdata-2023c-1.el9.noarch.rpm
3赞 RavinderSingh13 10/8/2023 #3

对于您显示的示例,请尝试遵循Perl单行解决方案。在此处使用正则表达式,它创建了 2 个捕获组,我们使用它们来打印值,同时根据所需的输出在此处执行替换。^(.*?)-([0-9]+[a-zA-Z]*(?:\.[0-9]+)*(?:-[a-zA-Z0-9]+)?)

perl -pe 's/^(.*?)-([0-9]+[a-zA-Z]*(?:\.[0-9]+)*(?:-[a-zA-Z0-9]+)?)/pkg: $1\tversion: $2/'  Input_file

评论

1赞 MaxU - stand with Ukraine 10/8/2023
谢谢你的回答!我不会在必须运行此命令的容器中使用 perl。我已经相应地更新了我的问题。
1赞 RavinderSingh13 10/9/2023
@MaxU-standwithUkraine,当然不是问题,我在这个线程(stackoverflow.com/a/77256052/5866580)下面添加了另一个答案,如果这对你有帮助,干杯。awk
3赞 pjh 10/9/2023 #4

如果你的容器有这个命令,那么它提供了最安全的方式来执行你想要的操作(如至少一个其他答案中所述)。rpm

如果您没有该命令,那么您可以根据 RPM 文件格式中所述的 RPM 包命名标准,相当轻松安全地执行您想要执行的操作。格式为 (NVRA)。由于内部连字符(破折号、字符)只能出现在 中,而 从不包含点,因此将包名称解析为其部分非常简单。rpmname-version-release.architecture-namearchitecture

便携式(无)解决方案是:-Esed

sed 's/\(.*\)-\(.*-[^.]*\).*/pkg: \1 version: \2/' pkgs.txt

也可以通过标准的 shell 字符串操作来完成。我(轻轻地)用和测试了这个 Shellcheck-clean 代码:bashdash

#! /bin/sh -

while read -r nvra || [ -n "$nvra" ]; do
    nv=${nvra%-*}
    ra=${nvra##*-}
    n=${nv%-*}
    v=${nv##*-}
    printf 'pkg: %s version: %s-%s\n' "$n" "$v" "${ra%%.*}"
done <pkgs.txt

不过,如果文件很大(我猜超过 1000 行),纯 shell 代码会明显变慢。pkgs.txt

3赞 RavinderSingh13 10/9/2023 #5

在这里添加一个答案,因为 OP 说可能不在他们的容器中。这里使用了GNU。awkperlawk

  • 根据所示样品制作字段分隔符。(\\+|\\.)el[0-9]+(\\.[0-9]*)*
  • 然后使用函数匹配正则表达式来获取此处的版本值。match-([0-9]+[a-zA-Z]*)([^-]+)(-[a-zA-Z0-9]+)?
  • 然后再次使用函数在版本之前获取值,如果支持延迟匹配:)会更容易做到matchawk
awk -F'(\\+|\\.)el[0-9]+(\\.[0-9]*)*' '
match($1,/-([0-9]+[a-zA-Z]*)([^-]+)(-[a-zA-Z0-9]+)?/,arr){
   val=arr[1] arr[2] arr[3]
}
match($1,val){
   print "pkg: " substr($1,1,RSTART-2) "\tversion: " val
}
'  Input_file

或者使用一个后的命令来获得好看格式的输出。columnawk

awk -F'(\\+|\\.)el[0-9]+(\\.[0-9]*)*' '
match($1,/-([0-9]+[a-zA-Z]*)([^-]+)(-[a-zA-Z0-9]+)?/,arr){
  val=arr[1] arr[2] arr[3]
}
match($1,val){
  print "pkg: " substr($1,1,RSTART-2) "\tversion: " val
}
' Input_file | column -t