Bash正则表达式匹配字符序列。

3

我正在编写一个bash脚本,它接收<task> ID字符串作为参数,然后执行指定的任务。在执行结束时,它必须显示总执行时间。代码必须灵活,并且易于添加新的条件。

用法:build.sh <task><task><task>...

任务ID有点棘手:

  • 如果只是一个字符[a-z],则表示给定任务的所有子任务
  • 如果是一个字符+数字,则仅表示特定的子任务

让我给你一些例子:

  • build.sh a1a2: 执行任务 a1a2,但不包括 a3
  • build.sh a: 执行所有以字符 a 开头的任务(a 的所有子任务)
  • build.sh a1b: 仅运行任务 a1 和所有子任务 b
  • build.sh a1b3: 仅运行任务 a1b3
  • build.sh ab: 运行所有子任务 ab
  • build.sh *: 运行构建脚本中定义的所有任务(所有子任务),如 a + b + c

任务的执行顺序是预定的,因此实际上 b1a1 将执行 a1b1

这是我的 bash 脚本:

#!/bin/bash -ue

# show help
function show_help() {
    if [ "$1" -eq 0 ]; then
        printf "..."
        exit 0
    fi
}

# does the build
function build {
    local title dir
    title="$1"
    dir="$2"
    printf "\n%b> building '%s'...%b\n" "$COLOR_YELLOW" "$title" "$COLOR_DEFAULT"
    cd "$WORKSPACE/../$dir"
    ./build.sh
    cd "$WORKSPACE"
}

# group A build
function build_group_a {
    if [[ "$1" == *a1* ]]; then build "task A.1" "base/xxx"; fi
    if [[ "$1" == *a2* ]]; then build "task A.2" "base/yyy"; fi
    if [[ "$1" =~ $(getRegexp a) ]]; then
      build "task A.1" "base/xxx"
      build "task A.2" "base/yyy"
    fi
}

# group B build
function build_group_b {
   # same with 'build_group_a' only conditions are different
}

# REGEXP THAT DOES NOT WORK PROPERLY
function getRegexp() {
    printf ".*%s[a-z].*" "$1"
}


show_help "$#"
START_TIME=$(date +%s)

# 'all' works but '*' not
if [[ "$1" == all ]]; then
    build_group_a a
    build_group_b b
else
    build_group_a "$1"
    build_group_b "$2"
fi

ELAPSED=$(($(date +%s) - START_TIME))
printf "build time: %s\n" "$(date -d@$ELAPSED -u +%H\ hour\ %M\ day\ %S\ sec)"

一切都正常工作,任务按顺序执行,但如果我使用build.sh a,则什么也不会执行。如果我使用build.sh ab,则只有任务a被执行。我不知道为什么。似乎我的正则表达式没有处理最后一个任务ID。

另一个问题是,如果我说build.sh aa1,那么所有a的任务都会被执行,但是任务a1会被执行两次。当调用a时,任务a1只能执行一次。

如果您可以指导我如何在正则表达式中解决这个问题,那将非常好。


1
这可能会有所帮助:如何调试bash脚本? - Cyrus
a失败是因为 .*a[a-z].* 的最小长度为2。ab.*b[a-z].*不匹配,所以b无法运行。 - jhnc
1
你可以通过将(如果a1,如果a2,如果a)更改为(如果a else(如果a1,如果a2))来停止运行aa1/a2 - jhnc
更好的正则表达式printf格式可能是%s([^0-9]|$) - jhnc
2个回答

2
在正则表达式部分,你可以尝试这样做:
#!/usr/bin/env bash

input="$1"
pattern='(\*|[[:alpha:]][[:digit:]]?)'
while [[ $input =~ $pattern ]]; do
    echo "Argument passed : ${BASH_REMATCH[1]}"
    input="${input:${#BASH_REMATCH[0]}}" # next iteration
done

使用以下内容进行测试:

bash build.sh a1b
bash build.sh "*"

好的,这就是为什么星号对我不起作用的原因。它必须放在括号之间。有趣。 - zappee

0
非常感谢您的评论。在您的帮助下,我成功修复了这个问题。 在最终版本中,我对正则表达式使用了稍微不同的方法。 同时,我还简化了所有的内容。
以下是包含正则表达式的代码:
#!/bin/bash -ue

function build {
    local arg id title dir group regexp
    arg=$(printf "%sz" "$1")
    id="$2"; title="$3"; dir="$4"; group=${id:0:1}
    regexp=$(printf "(0|%s|%s[a-z])" "$id" "$group")
    if [[ "$arg" =~ $regexp ]]; then
        printf "%b> building '%s'...%b\n" "$COLOR_YELLOW" "$title" "$COLOR_DEFAULT"
        cd "$WORKSPACE/.../$dir"
        ./build.sh
        cd "$WORKSPACE"
    fi
}

function show_help() {
    if [ "$1" -eq 0 ]; then
        printf "..."
        exit 0
    fi
}

...
show_help "$#"
START_TIME=$(date +%s)

build "$1" "a1" "task A.1" "aa/xxx"
build "$1" "a2" "task A.2" "aa/yyy"
build "$1" "b1" "task B.1" "bb/vvv"
build "$1" "b2" "task B.2" "cc/www"

ELAPSED=$(($(date +%s) - START_TIME))
printf "build time: %s\n\n" "$(date -d@$ELAPSED -u +%H\ hour\ %M\ day\ %S\ sec)"

这个效果很好,我认为它足够简单。

我使用0来运行所有任务,因为不幸的是,在我的正则表达式中星号(*)无法工作。 我不得不做的另一个技巧是在用户输入的末尾添加一个额外的字符(在我的情况下是z)。这是必要的,否则最后一个命令将不会被执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接