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


86

我在这里做错了什么?

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

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'

您实际使用的是哪个shell?/ bin / sh?/ bin / bash?/ bin / csh?
Willem Van Onsem 2013年

8
将正则表达式放在变量中比较安全。re='...whatever...'; [[ $string =~ $re ]](不带引号-这是极少数情况,它们会破坏没有它们的情况下会起作用的东西)。
查尔斯·达菲

3
请在作业两边加上单引号。双引号不能正确保护特殊字符。
Tripleee 2013年

查尔斯很多!可以不将其放入变量中也可以,但是绝对不能用引号将其引起来!例如:[[ $var =~ .* ]]用于匹配正则表达式.*(任何东西)。我想,如果你使用引号,自己被认为是正则表达式的一部分,报价...
斯特凡

4
我发现的问题摘要:(1.)pattern='^hello[0-9]*$'如果需要正则表达式匹配,请在双平方表达式中使用单引号(2.)将模式保存在变量中。不要引用该模式,因为用引号将regex模式匹配禁用。(即,表达式[[ "$x" =~ $pattern ]]将使用[[ "$x" =~ "$pattern" ]]regex进行匹配,并且表达式禁用regex匹配,并且等效于[[ "$x" == "$pattern" ]])。
Trevor Boyd Smith,

Answers:


177

关于bash的[[ ]]构造,有几件重要的事情要知道。首先:

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

第二件事:

还有一个附加的二进制运算符'=〜',...运算符右边的字符串被认为是扩展的正则表达式,并进行了相应的匹配...可以引用模式的任何部分以强制对其进行匹配作为字符串

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

因此,如果您输入:[[ $x =~ [$0-9a-zA-Z] ]]$0则在解释正则表达式之前,将先扩展右侧的正则表达式内部,这可能会导致正则表达式无法编译(除非$0以数字或标点符号结尾的扩展其ascii值小于一个数字)。如果您用-so引用右侧[[ $x =~ "[$0-9a-zA-Z]" ]],则右侧将被视为普通字符串,而不是正则表达式(并且$0仍将被扩展)。在这种情况下,您真正​​想要的是[[ $x =~ [\$0-9a-zA-Z] ]]

类似地,[[and之间]]的表达式在解释正则表达式之前被拆分为单词。因此,需要对正则表达式中的空格进行转义或引用。如果您想匹配字母,数字或空格,可以使用:[[ $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正则表达式语法“几乎可读”,但是仍然存在一些陷阱。(不是总在那儿吗?)一个原因是,即使一开始被引用,您也无法]输入,除非是在开始时。(这是Posix的正则表达式规则:如果要包含在字符类中,则需要从头开始。可以在头或尾开始,因此,如果您同时需要和,则需要以和开头,导致正则表达式“我知道我在做什么”表情:)$valid$valid]-]-]-[][-]


6
只想指出“!〜是”不匹配”运算符”是不正确的。无论使用if ! [[ $x =~ $y ]]还是if [[ ! $x =~ $y ]]
酒精饮料

shellchecker不同意...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
莱昂纳多

4
@leonard:与我的声明“您不能引用变量扩展”和注释“这不起作用”有什么不同?不清楚的是什么?
rici

1
@jinbeomhong:使用空格将表达式本身像平常一样分成单词。但是参数和命令扩展不是单词分割的。
rici

1
@jinbeomhong:我的意思与bash手册没什么不同。“的之间[[]]程序文本的被解析出来,以同样的方式的命令行被解析成词”。但是,与命令行不同,单词在扩展后不会拆分。
rici

26

万一有人想要使用变量的例子...

#!/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

13

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

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.