Bash if 语句中的正则表达式匹配

Posted

技术标签:

【中文标题】Bash if 语句中的正则表达式匹配【英文标题】:Regex matching in a Bash if statement 【发布时间】:2013-09-13 15:13:34 【问题描述】:

我在这里做错了什么?

尝试匹配任何包含空格、小写字母、大写字母或数字的字符串。特殊字符也不错,但我认为这需要转义某些字符。

TEST="THIS is a TEST title with some numbers 12345 and special char *&^%$#"

if [[ "$TEST" =~ [^a-zA-Z0-9\ ] ]]; then BLAH; fi

这显然只测试大写、小写、数字和空格。还是不行。

* 更新 *

我想我应该更具体一些。这是真正的代码行。

if [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; fi

* 更新 *

./anm.sh: line 265: syntax error in conditional expression
./anm.sh: line 265: syntax error near `&*#]'
./anm.sh: line 265: `  if [[ ! "$TITLE" =~ [a-zA-Z0-9 $%^\&*#] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; return; fi'

【问题讨论】:

您实际使用的是哪个外壳? /bin/sh? /bin/bash? /bin/csh? 将正则表达式放在变量中更安全。 re='...whatever...'; [[ $string =~ $re ]](不带引号——这是一种罕见的情况,他们会破坏没有引号的东西)。 请在作业两边加上单引号。双引号不能正确保护特殊字符。 非常感谢查尔斯!不把它放在一个变量中还是可以的,但它绝对不能被引用!例如:[[ $var =~ .* ]] 匹配正则表达式 .*(任何东西)。我猜如果你使用引号,引号本身被认为是正则表达式的一部分...... 我发现的问题总结:(1.) 如果您需要正则表达式匹配,请在双平方表达式中使用单引号 pattern='^hello[0-9]*$' (2.) 将模式保存在变量中,不要引用模式 因为引用禁用正则表达式模式匹配。 (即表达式 [[ "$x" =~ $pattern ]] 将使用正则表达式匹配,而表达式 [[ "$x" =~ "$pattern" ]] 禁用正则表达式匹配,等效于 [[ "$x" == "$pattern" ]])。 【参考方案1】:

我更愿意为此使用[:punct:]。此外,a-zA-Z09-9 可能只是 [:alnum:]

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]

【讨论】:

【参考方案2】:

关于 bash 的 [[ ]] 构造,有几件重要的事情需要了解。第一个:

[[]]之间的单词不进行分词和路径扩展;执行波浪号扩展、参数和变量扩展、算术扩展、命令替换、进程替换和引号删除。

第二件事:

一个额外的二元运算符'=~'可用,...运算符右侧的字符串被视为扩展正则表达式并进行相应匹配... 模式的任何部分都可以被引用强制将其作为字符串匹配

因此,=~ 两侧的$v 将扩展为该变量的值,但结果不会是单词拆分或路径名扩展。换句话说,在左侧不加引号的变量扩展是完全安全的,但您需要知道变量扩展将发生在右侧。

所以如果你写:[[ $x =~ [$0-9a-zA-Z] ]],那么右边的正则表达式里面的$0会在正则表达式被解释之前被扩展,这可能会导致正则表达式编译失败(除非$0的扩展结束带有 ascii 值小于数字的数字或标点符号)。 如果你像[[ $x =~ "[$0-9a-zA-Z]" ]]一样引用右侧,那么右侧将被视为普通字符串,而不是正则表达式(并且$0仍将被扩展)。在这种情况下你真正想要的是[[ $x =~ [\$0-9a-zA-Z] ]]

同样,[[]] 之间的表达式在解释正则表达式之前被拆分为单词。所以正则表达式中的空格需要转义或引用。如果您想匹配字母、数字或空格,您可以使用:[[ $x =~ [0-9a-zA-Z\ ] ]]。其他字符同样需要转义,例如#,如果没有引用,它将开始注释。当然,你可以将模式放入一个变量中:

pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...

对于包含大量需要转义或引用以通过 bash 词法分析器的字符的正则表达式,许多人更喜欢这种风格。但请注意:在这种情况下,您不能引用变量扩展:

# This doesn't work:
if [[ $x =~ "$pat" ]]; then ...

最后,我认为您要做的是验证变量是否仅包含有效字符。执行此检查的最简单方法是确保它不包含无效字符。换句话说,像这样的表达式:

valid='0-9a-zA-Z $%&#' # add almost whatever else you want to allow to the list
if [[ ! $x =~ [^$valid] ]]; then ...

! 否定测试,将其变成“不匹配”运算符,[^...] 正则表达式字符类表示“... 以外的任何字符”。

参数扩展和正则表达式运算符的组合可以使 bash 正则表达式语法“几乎可读”,但仍然存在一些陷阱。 (不是一直都有吗?)一个是你不能把] 放到$valid 中,即使$valid 被引用了,除非在最开始。 (这是 Posix 正则表达式规则:如果你想在字符类中包含 ],它需要放在开头。- 可以放在开头或结尾,所以如果你需要 ] 和 @ 987654348@,您需要以] 开头并以- 结尾,导致正则表达式“我知道我在做什么”表情:[][-])

【讨论】:

只想指出 "!~ 是“不匹配”运算符" 是不正确的。使用if ! [[ $x =~ $y ]]if [[ ! $x =~ $y ]] shellchecker 不同意...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex. @leonard:这与我的声明“你不能引用变量扩展”和评论“这不起作用”有什么不同?有什么不清楚的地方? @jinbeomhong:表达式本身像往常一样使用空格分隔成单词。但是参数和命令扩展不是分词的。 @jinbeomhong:我并没有说与 bash 手册有什么不同。 “[[]] 之间的单词”从程序文本中解析出来,就像命令行被解析成单词一样。不过,与命令行不同的是,单词在展开后不会被拆分。【参考方案3】:

如果有人想要一个使用变量的例子......

#!/bin/bash

# Only continue for 'develop' or 'release/*' branches
BRANCH_REGEX="^(develop$|release//*)"

if [[ $BRANCH =~ $BRANCH_REGEX ]];
then
    echo "BRANCH '$BRANCH' matches BRANCH_REGEX '$BRANCH_REGEX'"
else
    echo "BRANCH '$BRANCH' DOES NOT MATCH BRANCH_REGEX '$BRANCH_REGEX'"
fi

【讨论】:

【参考方案4】:

或者您可能正在查看这个问题,因为您碰巧像我一样犯了一个愚蠢的错字并将 =~ 反转为 ~=

【讨论】:

看来该模式必须至少以^ 开头并以美元符号$ 结尾才能工作。这是这样做的唯一方法,这样得到的真值对我来说是正确的。 (???) 我仍然设法在 javascript 中弄错了

以上是关于Bash if 语句中的正则表达式匹配的主要内容,如果未能解决你的问题,请参考以下文章

Perl:转义字符串中的特殊字符以匹配正则表达式

bash 正则表达式进阶--egrep

JS中的正则表达式匹配

bash 中的正则表达式量词——简单与扩展匹配 n 次

Perl学习笔记-2(正则表达式)

Bash case 语句中的正则表达式