【发布时间】:2016-05-18 11:11:46
【问题描述】:
尝试使用 Bash 内置的正则表达式匹配来解析以下类型的字符串,这些字符串将被转换为 Perl 替换表达式(引号不是数据的一部分)
'~#A#B#'
#^ ^ ^-- Replacement string.
#| +---- Pattern string.
#+------ Regular expression indicator (no need to escape strings A and B),
# which is only allowed if strings A and B are surrounded with ##.
# Strings A and B may not contain #, but are allowed to have ~.
'#A#B#'
#^------ When regex indicator is missing, strings A and B will be escaped.
'A#B'
# Simplified form of '#A#B#', i. e. without the enclosing ##.
# Still none of the strings A and B is allowed to contain # at any position,
# but can have ~, so leading ~ should be treated as part of string A.
我尝试了以下模式(同样,没有引号):
'^((~)?(#))?([^#]+)#([^#]+)\3$'
也就是说,它声明前导 ~# 是可选的(并且其中的 ~ 更加可选),然后捕获部分 A 和 B,并且要求尾随 # 仅在它出现时才存在出现在领导者身上。捕获前导 # 仅用于反向引用匹配 - 在其他地方不需要它,而捕获 ~ 以供脚本随后检查。
但是,该模式仅适用于最完整类型的输入数据:
'~#A#B#'
'#A#B#'
但不是为了
'A#B'
我。例如,每当缺少前导部分时,\3 就无法匹配。但是如果将\3替换为.*,则匹配成功,可以看出${BASH_REMATCH[3]}是一个空字符串。这是我不明白的,前提是未设置的变量在 Bash 中被视为空字符串。 那么我如何将反向引用与可选内容匹配?
作为一种解决方法,我可以编写一个替代模式
'^(~?)#([^#]+)#([^#]+)#$|^([^#]+)#([^#]+)$'
但它会为每种可能的情况生成不同的捕获组,这使得代码不太直观。
重要提示。正如@anubhava 在他的评论中提到的那样,反向引用匹配在某些 Bash 构建中可能不可用(也许这是构建选项的问题,而不是版本号,甚至是某些外部库)。这个问题当然是针对那些支持这种功能的 Bash 环境。
【问题讨论】:
-
试试
'^(~?#?)([^#]+)#([^#]+)\1$',或者如果~不需要在字符串的开头和结尾都检查,试试^~?(#?)([^#]+)#([^#]+)\1$ -
很抱歉,不太清楚,但只有在存在
#时才能出现前导~——它们不是两个独立的部分。 -
@WiktorStribiżew 不,你仍然没有得到它:如果
~已经存在,前面的#不能丢失,所以你的表达不适合我的任务,即使它适用于某些人输入。也就是说,它将~A#B拆分为('~' 'A' 'B')而不是 ('~A' 'B') — 当没有哈希包含字符串时,前导~没有特殊含义,必须被视为第一个字符串的一部分。 -
对不起,这个问题真的不清楚。请注意,一旦捕获的捕获组内容将被视为单个原子,并且反向引用将指向该文本。其目的是准确匹配匹配的内容。现在,您不能在 Bash 正则表达式中使用环视,只能在 Perl 中使用,因此无法在开头限制
~,也无法使用分支重置。我在这里看不到任何解决方法。
标签: regex bash backreference