尝试使用 bash 从文件名中提取子字符串和版本号

cyb*_*989 7 bash perl awk sed

我目前正在尝试使用 bash 从文件名中提取子字符串和版本号。

文件名有两种格式:

example-substring-1.1.0.tgz
example-substring-1.1.0-branch-name.tgz
Run Code Online (Sandbox Code Playgroud)

对于第一个场景,我能够使用 sed 提取版本号,如下所示:

echo example-substring-1.1.0.tgz | sed "s/.*-\(.*\)\.[a-zA-Z0-9]\{3\}$/\1/"
Run Code Online (Sandbox Code Playgroud)

然而,这不适用于第二种情况。

最终我想创建一个脚本,将第一个子字符串和版本存储在关联数组中,如下所示。

example_array["example-substring"]="1.1.0"
example_array["example-substring"]="1.1.0-branch-name"
Run Code Online (Sandbox Code Playgroud)

然而,这被证明很棘手,因为我似乎找不到适合这两种情况的好方法。对于版本包含分支名称的情况,我事先无法知道分支名称将包含多少个单词。

我认为变量扩展可能是可行的方法,但无法让它输出我想要的内容。

Ed *_*ton 6

为了能够真正测试这一点,我们需要包含更多有问题的情况的示例输入,例如-1.2.3看起来像分支名称中出现的版本号的字符串:

$ cat file
example-substring-foo-1.1.0.tgz
example-substring-bar-1.1.0-branch-name.tgz
example-substring-rainy-1.1.0-branch-1.2.3.tgz
Run Code Online (Sandbox Code Playgroud)

通常我会在 sed 或 awk 中执行模式匹配部分,例如使用任何 awk:

$ awk 'match($0,/-([0-9].*)\.[^.]+$/) {
    printf "\"%s\" \"%s\"\n", substr($0,1,RSTART-1), substr($0,RSTART+1)
}' file
"example-substring-foo" "1.1.0.tgz"
"example-substring-bar" "1.1.0-branch-name.tgz"
"example-substring-rainy" "1.1.0-branch-1.2.3.tgz"
Run Code Online (Sandbox Code Playgroud)

而不是 shell 循环,但因为您无论如何都想用结果填充 shell 数组:

$ cat tst.sh
#!/usr/bin/env bash

declare -A example_array

while IFS= read -r ver; do
    if [[ $ver =~ -([0-9].*)\.[^.]+$ ]]; then
        example_array["${ver::-${#BASH_REMATCH[0]}}"]="${BASH_REMATCH[1]}"
    fi
done < "$@"

for idx in "${!example_array[@]}"; do
    printf 'example_array["%s"]="%s"\n' "$idx" "${example_array[$idx]}"
done
Run Code Online (Sandbox Code Playgroud)

$ ./tst.sh file
example_array["example-substring-rainy"]="1.1.0-branch-1.2.3"
example_array["example-substring-bar"]="1.1.0-branch-name"
example_array["example-substring-foo"]="1.1.0"
Run Code Online (Sandbox Code Playgroud)


zdi*_*dim 3

使用 Perl

\n
echo "example-substring-1.1.0-branch-name.tgz" |\n    perl -wne'print join " ", /(.+)\\-([0-9]+\\.[0-9]+\\.[0-9]+.*)\\.tgz/'\n
Run Code Online (Sandbox Code Playgroud)\n

打印两个单词

\n
example-substring 1.1.0-branch-name\n
Run Code Online (Sandbox Code Playgroud)\n

因此,这就是它对 shell 脚本的返回,我认为将从该 shell 脚本中调用它,然后可以在 shell 脚本中形成所需的结构。\xe2\x80\xa0 也在没有分支名称和输入字符串的一些其他变体的情况下进行了测试。

\n

由于 也example-substring可以包含数字(为什么不?),分支名称也可以(为什么不?),因此正则表达式模式没有限制,前导部分和(可能的)尾随部分都简单地由.+和匹配.*

\n

但是我们需要更具体的版本号,我假设它始终由三个用点分隔的数字组成。我还假设了字符串的固定其余部分,即文件扩展名.tgz. 如果需要的话,这些可以稍微放松。

\n
\n

\xe2\x80\xa0 可以直接将一个列表(键值键值...)读入关联数组

\n
#!/bin/bash\n\neval declare -A ver=( $( \n    echo "example-substring-1.1.0-branch-name.tgz" | \n    perl -wnE'say join " ", /(.+)\\-([0-9]+\\.[0-9]+\\.[0-9]+.*)\\.tgz/' ))\n\necho ${ver["example-substring"]}\n
Run Code Online (Sandbox Code Playgroud)\n

或者先赋值给变量可能更合适

\n
str="example-substring-1.1.0-branch-name.tgz"\n\nread -r str val <<< $( \nperl -wE'say join " ", $ARGV[0] =~ /(.+)\\-([0-9]+\\.[0-9]+\\.[0-9]+.+)\\.tgz/' \n    -- "$str" )\n\nver[$str]=$val\n
Run Code Online (Sandbox Code Playgroud)\n

甚至只使用位置参数

\n
set -- $(\n    perl -wE'say join " ", $ARGV[0] =~ /(.+)\\-([0-9]+\\.[0-9]+\\.[0-9]+.+)\\.tgz/' \n        -- "$str" )\n\nver[$1]=$2\n
Run Code Online (Sandbox Code Playgroud)\n

当然还有其他方法可以将参数传递给 Perl 脚本或命令行程序(“one-liner”),以及在 bash 中获取其输出的其他方法。

\n

如果这段 Perl 代码需要注释,请告诉我。

\n