将正则表达式存储在 shell 变量中如何避免引用 shell 特有字符的问题?

将正则表达式存储在 shell 变量中如何避免引用 shell 特有字符的问题?

bash手册

将正则表达式存储在 shell 变量中通常是避免引用 shell 特有字符时出现问题的有用方法。有时很难在不使用引号的情况下按字面指定正则表达式,或者在注意 shell 的引号删除的同时跟踪正则表达式使用的引号。使用 shell 变量来存储模式可以减少这些问题。例如,以下内容是等效的:

pattern='[[:space:]]*(a)?b'
[[ $line =~ $pattern ]]

[[ $line =~ [[:space:]]*(a)?b ]]

如果要匹配正则表达式语法中特殊的字符,则必须将其加引号以删除其特殊含义。这意味着在模式中xxx.txt.匹配字符串中的任何字符(其通常的正则表达式含义),但在模式中"xxx.txt"它只能匹配文字.。 shell 程序员应特别注意反斜杠,因为 shell 和正则表达式都使用反斜杠来删除后面字符的特殊含义。以下两组命令并不等效:

pattern='\.'

[[ . =~ $pattern ]]
[[ . =~ \. ]]

[[ . =~ "$pattern" ]]
[[ . =~ '\.' ]]

前两个匹配会成功,但后两个不会成功,因为在后两个中,反斜杠将成为要匹配的模式的一部分。在前两个示例中,反斜杠删除了 的特殊含义.,因此文字.匹配。如果第一个示例中的字符串不是.,例如a,则模式将不匹配,因为.模式中的引号失去了匹配任何单个字符的特殊含义。

将正则表达式存储在 shell 变量中如何成为避免引用 shell 特有字符时出现问题的有用方法?

给出的例子似乎没有解释这一点。在给定的示例中,一个方法中的正则表达式文字和pattern另一种方法中的 shell 变量的值相同。

谢谢。

答案1

[[ ... ]]标记化与正则表达式冲突(更多内容参见my answer to your follow-up question) and \ is overloaded as a shell quoting operator and a regexp operator (with some interference between the two in bash), and even when there's no apparent reason for a clash, the behaviour can be surprising. Rules can be confusing.

谁能在不尝试(在所有可能的输入上)任何给定版本的情况下知道它们会做什么bash

[[ $a = a|b ]]
[[ $a =~ a|b ]]
[[ $a =~ a&b ]]
[[ $a =~ (a|b) ]]
[[ $a =~ ([)}]*) ]]
[[ $a =~ [/\(] ]]
[[ $a =~ \s+ ]]
[[ $a =~ ( ) ]]
[[ $a =~ [ ] ]]
[[ $a =~ ([ ]) ]]

您不能引用正则表达式,因为如果这样做,从 bash 3.2 开始,如果尚未启用 bash 3.1 兼容性,则引用正则表达式会删除 RE 运算符的特殊含义。例如,

[[ $a =~ 'a|b' ]]

$a如果仅包含文字则匹配a|b

将 regexp 存储在变量中可以避免所有这些问题,并且还使代码与ksh93和兼容zsh(前提是您将自己限制为 POSIX ERE):

regexp='a|b'
[[ $a =~ $regexp ]] # $regexp should *not* be quoted.

该 shell 命令的解析/标记化没有歧义,并且使用的正则表达式是存储在变量中的正则表达式,无需任何转换。

答案2

匹配显式字符串的唯一方法是引用它:

[[ $var =~ 'quux' ]]

即使字符串包含特殊字符(对于 shell 来说是特殊的)[A])
无需 shell 扩展或解释它们[b]:

$ var='^abcd'
$ [[ $var =~ '^ab' ]] && echo yes || echo no
yes

如果我们实际上需要允许(shell)特殊字符并允许 shell 将它们解释为正则表达式,则应该不加引号。

$ var='abcd'
$ [[ $var =~ ^ab ]] && echo yes || echo no
yes

但不带引号的字符串会产生新问题,例如空格:

$ var='ab cd'
$ [[ $var =~ ^ab cd ]] && echo yes || echo no
bash: syntax error in conditional expression
bash: syntax error near `cd'

为了解决这个问题,我们仍然需要引用特殊字符:

$ var='ab cd'
$ [[ $var =~ ^"ab cd" ]] && echo yes || echo no
yes

$ [[ $var =~ ^ab\ cd ]] && echo yes || echo no
yes

其他例子:

[[ "a b"  =~  ^a\ b$ ]] && echo yes
[[ "a|b"  =~  ^a\|b$ ]] && echo yes
[[ "a&b"  =~  ^a\&b$ ]] && echo yes

将正则表达式存储在变量中可以避免所有这些引用问题。

$ regex='^a b$'
$ [[ "a b" =~ $regex ]] && echo yes
yes

[A] shell 特殊字符列表 ( | & ; ( ) < > space tab newline)。

[b] 这是真实的自 bash 版本 bash-3.2-alpha 起(在“3. Bash 中的新功能”标题下)

F。现在,与其他模式匹配运算符一样,将字符串参数引用到 [[ 命令的 =~ 运算符会强制进行字符串匹配。


备份bash FAQ 的扩展描述:

E14) 为什么将模式参数引用到正则表达式匹配条件运算符 (=~) 会导致正则表达式匹配停止工作?

在 bash-3.2 之前的 bash 版本中,未指定将正则表达式参数引用到 [[ 命令的 =~ 运算符的效果。实际效果是,双引号模式参数需要反斜杠来引用特殊模式字符,这会干扰双引号单词扩展执行的反斜杠处理,并且与 == shell 模式匹配运算符处理引号字符的方式不一致。

在 bash-3.2 中,shell 更改为在 =~ 运算符的单引号和双引号字符串参数中内部引用字符,这抑制了正则表达式处理所特有的字符的特殊含义('.'、'['、 '\'、'('、')'、'*'、'+'、'?'、'{'、'|'、'^' 和 '$') 并强制它们按字面匹配。这与“==”模式匹配运算符处理其模式参数的引用部分的方式一致。

由于引用字符串参数的处理方式发生了变化,出现了几个问题,其中最主要的是模式参数中的空格问题以及 bash-3.1 和 bash-3.2 之间引用字符串的不同处理方式。这两个问题都可以通过使用 shell 变量来保存模式来解决。由于在 [[ 命令的所有操作数中展开 shell 变量时不会执行分词,因此用户可以在分配变量时按照自己的意愿引用模式,然后将值展开为可能包含空格的单个字符串。第一个问题可以通过使用反斜杠或任何其他引用机制来转义模式中的空白来解决。

相关问题:

在正则表达式中使用变量

相关内容