我想确定多行字符串是否以包含指定模式的行结尾。
这些代码失败,不匹配。
s=`echo hello && echo world && echo OK`
[[ "$s" =~ 'OK$' ]] && echo match
答案1
在bash
3.2 或更高版本中,如果未启用与 3.1 的兼容性(使用选项compat31
或BASH_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
,zsh
或ksh93
:
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_ENDONLY
给pcre_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 模式,而不是正确的正则表达式。