1

我正在尝试在 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),我可以看到 INPUT 字符串匹配,因为代码进入 IF 但 BASH_REMATCH 没有填充我的两个捕获组。

我上线了:

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

可能是什么问题?

稍后编辑


接受的答案

接受最具解释性的答案。

什么最终解决了这个问题:

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

4

2 回答 2

3

正如 Cyrus 在他的回答中显示的那样,您的代码的简化版本(具有相同的输入)原则上可以在 Linux 上运行。

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

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

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


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

  • 您的shebang 行读取#!/usr/bin/env /bin/bash实际上与#!/bin/bash.

    • /usr/bin/env如果您想要一个版本不是/bin/bash执行,通常使用它,您稍后安装并放入 PATH(也):
      #!/usr/bin/env bash

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

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

  • =~是少数依赖于平台的Bash 功能之一:它使用平台的正则表达式库实现的特定正则表达式方言。

    • \s是一种字符类快捷方式,并非在所有平台上都可用,尤其是在 macOS 上不可用;与POSIX 兼容的等效项是[[:space:]].

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

  • 最好不要使用全大写的shell变量名INPUT以免与环境变量和特殊的shell变量发生冲突

于 2017-03-31T18:55:32.903 回答
1

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

$ 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:]]\dwith[[:digit:]]等替换,就可以了。如果您避免使用 RE 快捷方式,您的表达式将被更广泛地理解。

于 2017-03-31T19:19:34.167 回答