我的问题来自将正则表达式存储在 shell 变量中如何避免引用 shell 特有字符的问题?。
为什么会出现错误:
$ [[ $a = a|b ]] bash: syntax error in conditional expression: unexpected token `|' bash: syntax error near `|b'
[[ ... ]]
的第二个操作数内部=
预计是一个通配模式。不是
a|b
有效的通配符模式吗?你能指出它违反了哪条语法规则吗?下面的一些评论指出它
|
被解释为管道。然后将
=
glob 模式更改为=~
regex 模式使|
工作$ [[ $a =~ a|b ]]
我从中学到了学习狂欢p180 在我之前的帖子
|
在解释开始时,甚至在任何其他解释步骤(包括解析示例中的条件表达式)之前,它都被识别为管道。那么如何|
在使用时被识别为正则表达式运算符=~
,而不会像使用时一样在无效使用时被识别为管道=
呢?这让我认为第 1 部分中的语法错误并不意味着|
被解释为管道。shell 从标准输入或脚本读取的每一行称为管道;它包含一个或多个由零个或多个竖线字符 (|) 分隔的命令。对于它读取的每个管道,shell 将其分解为命令,设置管道的 I/O,然后对每个命令执行以下操作(图 7-1):
谢谢。
答案1
没有充分的理由
[[ $a = a|b ]]
应该报告错误而不是测试 $a 是否是字符串a|b
,而 while[[ $a =~ a|b ]]
不返回错误。
唯一的原因是它|
通常(外部和内部[[ ... ]]
)是一个特殊字符。在那个[[ $a =
位置上,bash
需要一种正常的令牌类型单词就像普通 shell 命令行中的参数或重定向目标一样(但就像extglob
自 bash 4.1 以来已启用该选项一样)。
(经过单词在这里,我指的是单词在假设的 shell 语法中,例如POSIX 规范描述的一种,shell 会将其解析为简单 shell 命令行中的一个标记,而不是其他单词定义,例如英语字母序列或非空格字符序列。foo"bar baz"
, $(echo x y)
, 是这样两个单词s)。
在普通的 shell 命令行中:
echo a|b
通过echo a
管道传送到b
.a|b
不是一个单词,它是三个标记:aa
单词,一个|
令牌和一个b
单词令牌。
当用于 时[[ $a = a|b ]]
,bash
期望单词它得到 ( a
),但随后发现一个意外的|
标记,导致错误。
有趣的是,bash
没有抱怨:
[[ $a = a||b ]]
因为它现在是一个a
标记,后面跟着一个||
标记,后面跟着b
,所以它的解析方式与:
[[ $a = a || b ]]
这是测试字符串是否$a
为空。a
b
现在,在:
[[ $a =~ a|b ]]
bash
不能有相同的解析规则。具有相同的解析规则意味着上面的内容会出错,并且需要引用该规则|
以确保a|b
是单个单词。但是,从 bash 3.2 开始,如果你这样做:
[[ $a =~ 'a|b' ]]
这不再是针对a|b
正则表达式匹配,而是针对a\|b
正则表达式匹配。也就是说,shell 引用具有删除正则表达式运算符的特殊含义的副作用。这是一项功能,因此行为与[[ $a = "?" ]]
此类似,但通配符模式(在 中使用[[ $a = pattern ]]
)是 shell字(例如在 glob 中使用),而正则表达式则不然。
因此,在解析运算符的参数时,bash
必须以不同的方式对待所有扩展的正则表达式运算符,这些运算符通常是特殊的 shell 字符,例如|
, (
。)
=~
不过,请注意,虽然
[[ $a =~ (ab)*c ]]
现在有效,
[[ $a =~ [)}] ]]
没有。你需要:
[[ $a =~ [\)}] ]]
[[ $a =~ [')'}] ]]
在以前的版本中,bash
反斜杠会错误地匹配。这个问题已经解决了,但是
[[ $a =~ [^]')'] ]]
做不是例如,像应该那样匹配反斜杠。因为bash
无法意识到 是)
在括号内,所以转义)
会导致匹配除,和[^]\)]
之外的任何字符的正则表达式。]
\
)
ksh93
在这方面有更严重的错误。
在 中zsh
,它是一个正常的 shell 单词,并且引用正则表达式运算符不会影响正则表达式运算符的含义。
[[ $a =~ 'a|b' ]]
与正则表达式匹配a|b
。
这意味着=~
也可以将其添加到[
/test
命令中:
[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'
(也可以在 . 中使用yash
。需要在as=~
中引用,因为那里是一个特殊的 shell 运算符)。zsh
=something
bash 3.1 过去的行为类似于zsh
.它在 3.2 中发生了变化,大概是为了与 一致ksh93
(尽管bash
是第一个提出的 shell [[ =~ ]]
),但您仍然可以执行BASH_COMPAT=31
或shopt -s compat31
恢复到以前的行为(除了 while[[ $a =~ a|b ]]
在 3.1 中会返回错误bash
,它不再bash -O compat31
与较新版本的) 一起使用bash
。
希望它能澄清为什么我说这些规则令人困惑以及为什么使用:
[[ $a =~ $var ]]
有助于包括向其他 shell 的可移植性。
答案2
标准 glob(“文件名扩展”)是:*
、?
和[ ... ]
.|
在标准(非 extglob)设置中不是有效的 glob 运算符。
尝试:
shopt -s extglob
[[ a = @(a|b) ]] && echo matched
答案3
如果你想要正则表达式匹配,测试将是:
[[ "$a" =~ a|b ]]