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