shell测试多行字符串最后一行是否包含指定模式

shell测试多行字符串最后一行是否包含指定模式

我想确定多行字符串是否以包含指定模式的行结尾。

这些代码失败,不匹配。

s=`echo hello && echo world && echo OK`
[[ "$s" =~ 'OK$' ]] && echo match

答案1

bash3.2 或更高版本中,如果未启用与 3.1 的兼容性(使用选项compat31BASH_COMPAT=3.1),则引用正则表达式运算符(不仅与\任何bash引用运算符 ( '...', "...", $'...', $"...") 一起使用)将删除其特殊含义。

[[ $var =~ 'OK$' ]]

仅匹配包含OK$字面量的字符串(与$字面量匹配$

[[ $var =~ OK$ ]]

匹配以 结尾的字符串OK(即$在字符串末尾匹配的 RE 运算符)。

这也适用于存储在变量中的正则表达式或某些替换的结果。

[[ $var =~ $regexp ]]   # $var matches $regexp
[[ $var =~ "$string" ]] # $var contains $string

请注意,这可能会变得很尴尬,因为您需要为 shell 语法引用一些字符(如不匹配时的空格、、、、括号)<。例如,如果您想匹配正则表达式(3 个字符后跟 a 、 a 或和a ),您需要类似以下内容:>&.{3} <> [)}]&" <> ")}&

[[ $var =~ .{3}" <> "[}\)]\& ]]

如果不确定哪些字符需要引用,你总是可以使用临时变量。这也意味着它将使代码兼容bash31,zshksh93

pattern='.{3} <> [})]&'
[[ $var =~ $pattern ]] # remember *not* to quote $pattern here

这也是您可以利用系统正则表达式的非 POSIX 扩展运算符的唯一方法(不使用选项compat31(或))。BASH_COMPAT=3.1

例如,为了\<在许多正则表达式引擎中将其视为单词边界,您需要:

pattern='\<word\>'
[[ $var =~ $pattern ]]

正在做:

[[ $var =~ \<word\> ]]

不会像bash将它们\视为 shell 引用运算符并在传递<word>到正则表达式库之前剥离它们一样工作。

请注意,ksh93 的情况要糟糕得多,其中:

[[ $var =~ "x.*$" ]]

例如将匹配 onwhatever-xa*但不匹配whatever-xfoo。上面的引用删除了 的特殊含义*,但没有删除.Nor 的特殊含义$

行为zsh更简单:引用不会改变正则表达式运算符的含义(如 bash31 中),这使得行为更可预测(它也可以使用 PCRE 正则表达式而不是 ERE(带有set -o rematchpcre))。

yash没有[[...]]构造,但它的[内置有一个=~运算符(也在 中zsh)。当然,[作为普通命令,引用不会影响正则表达式运算符的解释方式。


另请注意,严格来说,您的$s不包含 3 行,而是包含 2 个完整行,后跟一个未终止的行。它包含hello\nworld\nOK。在OK$扩展正则表达式中,$运算符只会匹配末尾的细绳

在 3 整行中细绳,就像hello\nworld\nOK\n(你无法通过命令替换来获得命令替换条全部尾随换行符),$会在 , 之后匹配\n,因此OK$不会匹配它。

然而zsh -o pcrematch$如果有的话,字符串末尾和换行符之前的匹配都会匹配,因为它没有将标志传递PCRE_DOLLAR_ENDONLYpcre_compile.这可能被视为一个坏主意,因为通常 shell 中的变量不包含尾随换行符,当它们包含时,我们通常希望它们被视为数据

答案2

至少在 中bash,引用 RHS 会强制将其视为字符串比较

$ s=$(printf 'hello\nworld\nOK\n')
$ echo "$s"
hello
world
OK
$ [[ "$s" =~ OK$ ]] && echo "match" || echo "no match"
match

然而

$ s=$(printf 'hello\nworld\nOK$\n')
$ echo "$s"
hello
world
OK$
$ [[ "$s" =~ 'OK$' ]] && echo "match" || echo "no match"
match

答案3

鲜为人知的事实:case也是如此。

case "$(printf 'hello\nworld\nOK\n')" in
  *$'\nOK') echo "match";;
  *) echo "no match";;
esac

“C 风格”字符串$'...'是 Bash 扩展(它提供了一个上下文,其中反斜杠转义代码可以\n在 shell 字符串中使用),但为了可移植性,您可以说

*"
OK") echo "match";;

获得完全兼容 POSIX 的 shell 脚本。

不过,语句中可用的模式case是 shell glob 模式,而不是正确的正则表达式。

相关内容