我得到了一些包含正则表达式特殊控制字符的文件名。
我需要准备一个正则表达式,从字面上考虑所有这些字符。
简化的测试用例:
strFilenameOnDB="some ( file ) name +.ok";
strFilenameToCheck="$strFilenameOnDB"; #code simplification
strRegex=".*${strFilenameToCheck}.*";
if [[ "$strFilenameOnDB" =~ $strRegex ]];then echo OK;fi
上面的方法(当然)会失败。
在 Perl 中我们可以使用 /Q /E (https://stackoverflow.com/a/3971923/1422630)将扩展的 $strRegex 转换为文字,bash 有类似的东西吗?
Obs.:我会发布我已经在做的事情,但我想知道是否有更好的方法?
答案1
在 Bash 的=~
匹配运算符中,可以通过将正则表达式中的文字字符串放在双引号内来指定它们。
所以理论上你只需要将 Perl 的 \Q 和 \E 分别变成一个双引号即可。
但是,如果您的要求是使用部分可变(即包含要扩展的其他 shell 变量)和部分文字的正则表达式,并且它本身包含在 shell 变量中,那么恐怕唯一的出路是到还使用eval
。
也就是说,您的示例代码将变成这样:
strFilenameOnDB="some ( file ) name +.ok";
strFilenameToCheck="$strFilenameOnDB"; #code simplification
strRegex=".*\"${strFilenameToCheck}\".*"; # <<--- note the backslash before each _inner_ double-quote: this is Bash’s syntax to embed a literal double-quote in a string _made by_ double-quotes
# then we shall use eval on the whole test operation
if eval '[[ "${strFilenameOnDB}" =~ '"${strRegex}"' ]]';then echo OK;fi
# or, using a fine Bash’s shortcut:
eval '[[ "${strFilenameOnDB}" =~ '"${strRegex}"' ]]' && echo OK
总而言之,为了将文字字符串嵌入到 shell 变量中包含的部分变量正则表达式中,您需要:
- 使用
\"
and another\"
代替 Perl 的 \Q 和 \E - 将整个测试命令嵌入到仔细引用的内容中
eval
为了扩展包含正则表达式的字符串,所有这些都是必需的第一的,这样 shell 变量中的两个"
被视为正则表达式的文字部分的开始和结束,而不是通常的 Bash 引用字符,然后对这样的结果模式执行整个匹配操作。
(当你必须包括双引号或者反斜杠在双引号 shell 变量内的正则表达式中..)
顺便说一句,您实际上不需要.*
正则表达式的开头和结尾,因为这些通常隐含在 Bash 的正则表达式操作中。事实上,^
当$
您不想要暗示正则表达式之前和之后的其他字符。
答案2
您只是想查看文件名是否包含特定的子字符串吗?因为如果您使用 执行此操作[[ =~ ]]
,则不需要前导部分和尾随.*
部分:正则表达式匹配更像是搜索,在字符串中的任何位置找到匹配就足够了。
此外,在 Bash 中,引用模式(或包含模式的变量)(部分)会删除引用字符的特殊含义。因此,例如这将匹配:
re=' + '
[[ "foo + doo" =~ "$re" ]] && echo match
而这不是(加号现在很特殊并且与本身不匹配):
re=' + '
[[ "foo + doo" =~ $re ]] && echo match
相比之下,非正则表达式匹配需要匹配整个字符串,因此您需要一个前导和一个尾随*
:
pattern=' * '
[[ "foo * doo" = *"$pattern"* ]] && echo match
答案3
就我个人而言,我不会将您希望作为文字的字符串与您希望解释为正则表达式模式的正则表达式位组合起来。表达式的文字字符串位应加双引号,需要解释为正则表达式的位不应加双引号。
[[ $strFilenameOnDB =~ .*"$strFilenameToCheck".* ]] && echo OK
但在这种情况下,由于默认情况下正则表达式并不锚定到字符串的开头或结尾(与始终匹配的文件名通配模式不同)完全的字符串),你可以完全没有侧翼.*
。
答案4
我这样改变匹配器:
sedExact='s"(.)"[\1]"g';
strRegex=".*$(echo "$strFilenameToCheck" |sed -r "$sedExact").*";