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 语句中的正则表达式匹配的主要内容,如果未能解决你的问题,请参考以下文章