BASH_REMATCH空

BASH_REMATCH empty

我正在尝试捕获 Bash 中的某些输入正则表达式,但 BASH_REMATCH 变为 EMPTY

#!/usr/bin/env /bin/bash
INPUT=$(cat input.txt)
TASK_NAME="MailAccountFetch"

MATCH_PATTERN="(${TASK_NAME})\s+([0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{2}:[0-9]{2}:[0-9]{2})"

while read -r line; do
    if [[ $line =~ $MATCH_PATTERN ]]; then
        TASK_RESULT=${BASH_REMATCH[3]}
        TASK_LAST_RUN=${BASH_REMATCH[2]}
        TASK_EXECUTION_DURATION=${BASH_REMATCH[4]}
    fi
done <<< "$INPUT"

我的输入是:

    MailAccountFetch                         2017-03-29 19:00:00  Success      5.0 Second(s)      2017-03-29 19:03:00

通过调试脚本 (VS Code+Bash ext),当代码进入 IF 时,我可以看到 INPUT 字符串匹配,但是 BASH_REMATCH 没有填充我的两个捕获组。

我在:

GNU bash, version 4.4.0(1)-release (x86_64-pc-linux-gnu)

可能是什么问题?

稍后编辑


已接受答案

接受最具解释性的回答。

最终解决问题的方法:

bashdb/VS 代码环境导致空 BASH_REMATCH。 单独 运行 时代码工作正常。

正如赛勒斯在他的回答中所展示的那样,您的代码的简化版本 - 使用相同的输入 - 原则上 Linux

就是说,您的代码引用了捕获组 34,而您的正则表达式仅定义了 2.

换句话说:${BASH_REMATCH[3]}${BASH_REMATCH[4]} 根据定义为空。

但是请注意,如果 =~ 表示成功,BASH_REMATCH 永远不会 完全 空:至少 - 在没有任何捕获的情况下组 - ${BASH_REMATCH[0]} 将被定义。


有一些一般性的观点值得提出:

  • 您的 shebang 行 显示为 #!/usr/bin/env /bin/bash实际上与 #!/bin/bash[=90= 相同].

    • /usr/bin/env 通常用于执行 other 而不是 /bin/bash 的版本,您稍后安装的版本和放入 PATH(也):
      #!/usr/bin/env bash

    • ghoti 指出使用 #!/usr/bin/env bash 的另一个原因是还支持不太常见的平台,例如 FreeBSD,如果安装了 bash,则位于在 /usr/local/bin 而不是通常的 /bin.

    • 在任何一种情况下,都很难预测将执行哪个 bash 二进制文件,因为它取决于调用时的有效 $PATH 值。

  • =~ 是为数不多的 Bash 功能之一 平台相关:它使用由平台的正则表达式库实现的特定正则表达式方言。

    • \s 是字符 class 快捷方式,并非在所有平台上都可用,特别是在 macOS 上; POSIX 兼容的等价物是 [[:space:]].

    • (在您的特定情况下,\s 应该有效,但是,因为您的 Bash --version 输出表明您在 Linux发行版。)

  • 最好不要使用全大写的shell变量名INPUT,这样avoid conflicts with environment variables and special shell variables.

Bash使用系统库解析正则表达式,不同的解析器实现不同的特性。您遇到了正则表达式 class shorthand 字符串不起作用的地方。请注意以下事项:

$ s="one12345   two"
$ [[ $s =~ ^([a-z]+[0-9]{4})\S*\s+(.*) ]] && echo yep; declare -p BASH_REMATCH
declare -ar BASH_REMATCH=()
$ [[ $s =~ ^([a-z]+[0-9]{4})[^[:space:]]*[[:space:]]+(.*) ]] && echo yep; declare -p BASH_REMATCH
yep
declare -ar BASH_REMATCH=([0]="one12345   two" [1]="one1234" [2]="two")

我也在 macOS 上这样做,但我在 FreeBSD 上得到了相同的行为。

只需将 \s 替换为 [[:space:]],将 \d 替换为 [[:digit:]],等等,您就可以开始了。如果您避免使用 RE 快捷方式,您的表达方式将会得到更广泛的理解。