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

fbcarpbf  于 11个月前  发布在  Perl
关注(0)|答案(5)|浏览(98)

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

example-substring-1.1.0.tgz
example-substring-1.1.0-branch-name.tgz

字符串
对于第一个场景,我可以使用sed提取版本号,如下所示:

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


但这对第二种情况不起作用。
最后,我想创建一个脚本,将第一个子字符串和版本存储在一个关联数组中,如下所示。

example_array["example-substring"]="1.1.0"
example_array["example-substring"]="1.1.0-branch-name"


这是棘手的,但是,我似乎找不到一个好的方法,将两种情况下工作。和场景的版本包括分支名称,我不能事先知道有多少个字的分支名称将组成。
我认为变量扩展可能是要走的路,但不能让它输出我想要的。
如果你能帮忙的话,我将不胜感激。

oug3syen

oug3syen1#

为了能够真正测试这一点,我们需要包含更多问题案例的样本输入,例如,像-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

字符串
通常我会在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"


而不是一个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

$ ./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"

vu8f3i0k

vu8f3i0k2#

用Perl

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

字符串
打印两个字

example-substring 1.1.0-branch-name


因此,这是它返回到shell脚本,我假设这将被调用,然后可以在shell脚本中形成所需的结构。†测试也没有分支名称,并使用输入字符串的一些其他变体。
由于example-substring也可以包含数字(为什么不呢?),分支名称也可以(为什么不呢?),因此正则表达式模式没有任何限制,并且前导和(可能的)尾部都可以简单地由.+.*匹配。
但是我们需要更具体的版本号,我使用了一个假设,它总是由 * 3 * 个用点分隔的数字组成。我还假设了字符串的固定其余部分,文件扩展名.tgz。如果需要,这些可以放宽一些。
†可以直接将列表(键值键值...)读入关联数组

#!/bin/bash

eval declare -A ver=( $( 
    echo "example-substring-1.1.0-branch-name.tgz" | 
    perl -wnE'say join " ", /(.+)\-([0-9]+\.[0-9]+\.[0-9]+.*)\.tgz/' ))

echo ${ver["example-substring"]}


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

str="example-substring-1.1.0-branch-name.tgz"

read -r str val <<< $( 
perl -wE'say join " ", $ARGV[0] =~ /(.+)\-([0-9]+\.[0-9]+\.[0-9]+.+)\.tgz/' 
    -- "$str" )

ver[$str]=$val


或者仅仅使用位置参数

set -- $(
    perl -wE'say join " ", $ARGV[0] =~ /(.+)\-([0-9]+\.[0-9]+\.[0-9]+.+)\.tgz/' 
        -- "$str" )

ver[$1]=$2


当然,还有其他方法可以将参数传递给Perl脚本或命令行程序(“一行程序”),以及其他方法可以在bash中获取其输出。
如果这段Perl代码需要注解,请告诉我。

ryevplcw

ryevplcw3#

如果您愿意使用grep而不是sed,那么lookaheads和lookbehinds将允许您定义模式来提取您关心的内容。
考虑一下这个模式:.+(?=-\d+\.\d+\.\d+)这将匹配任何后跟-<numbers>.<numbers>.<numbers>的内容。?=标记一个条件先行,这是一个必须匹配下一个字符的表达式,但被排除在模式的最终匹配之外。当与您的示例一起使用时:

$ echo example-substring-1.1.0.tgz | grep -Po '.+(?=-\d+\.\d+\.\d+)'
example-substring
$ echo example-substring-1.1.0-branch-name.tgz | grep -Po '.+(?=-\d+\.\d+\.\d+)'
example-substring

字符串
(The P标志启用PCRE 2,o标志仅打印匹配项)
还考虑模式:(?<=-)\d+\.\d+\.\d+.*(?=\.tgz$)它使用lookbehinds来Assert,紧接在模式之前,有一个-,并使用lookaheads来Assert模式以.tgz结束。当用于您的示例时:

echo 'example-substring-1.1.0.tgz' | grep -Po '(?<=-)\d+\.\d+\.\d+.*(?=\.tgz$)'
1.1.0
$ echo 'example-substring-1.1.0-branch-name.tgz' | grep -Po '(?<=-)\d+\.\d+\.\d+.*(?=\.tgz$)'
1.1.0-branch-name

brqmpdu1

brqmpdu14#

这可能对你有用(GNU sed):

sed -E 's/^([^-]+-)+([0-9.]+).*\..*/\2/' file

字符串
匹配文件名,其中包含一个或多个由-分隔的单词,后跟由.分隔的数字,然后以.开头的扩展名结束,并返回由.分隔的数字。

gfttwv5a

gfttwv5a5#

使用Bash内置的模式匹配,你可以做你需要做的事情。这个Shellcheck-clean代码演示了这个想法:

#! /bin/bash -p

shopt -s extglob

files=( example-substring-1.1.0.tgz example-substring2-1.1.0-branch-name.tgz )

declare -A example_array

for f in "${files[@]}"; do
    base=${f%.*}    # remove suffix
    substring=${base%%-+([0-9]).*}
    example_array["$substring"]=${base#"$substring-"}
done

declare -p example_array

字符串
这将产生:

declare -A example_array=([example-substring2]="1.1.0-branch-name" [example-substring]="1.1.0" )

  • shopt -s extglob启用“扩展globbing”(包括像+([0-9])这样的模式)。请参阅glob - Greg's Wiki中的extglob部分。
  • 有关${f%.*}${base%%-+([0-9]).*}${base#"$substring-"}的解释,请参阅删除字符串的一部分(BashFAQ/100(如何在bash中进行字符串操作?))。
  • 一般来说,declare -p var以一种明确的方式打印变量的值。在打印两种数组的值时,它避免了循环和陷阱。

相关问题