Bash HereDoc标记词正则捕获异常:为何仅匹配OF而非EOF?
Bash HereDoc标记词正则匹配问题解析
问题场景
Bash里HereDoc的开头通常是cat <<EOF或cat << EOF,即<<和标记词(比如EOF)之间可带空格也可不带。想用PCRE正则^\s*cat.*[^<]<{2}[^<](.*)捕获标记词,但测试发现:当<<和EOF之间无空格时,只能捕获到OF而非完整的EOF;同时需要确保正则只匹配恰好两个<,不能匹配三个及以上的情况。
问题原因
你的正则里[^<]<{2}[^<]这部分是核心问题:
.*是贪婪匹配,会尽可能多吃掉字符,直到后面的[^<]<{2}能匹配。处理cat <<EOF时,.*会匹配到cat,接着[^<]匹配这个空格,<{2}匹配<<。- 此时后面的
[^<]会匹配<<之后的第一个字符E(因为E不是<,符合[^<]的规则),导致捕获组(.*)只能拿到剩下的OF,相当于第一个字符被“吃掉”了。 - 原正则也没限制三个及以上
<的情况,比如cat <<<EOF会被错误匹配——[^<]<{2}会直接匹配最后两个<,忽略第一个。
修正后的正则
要实现仅匹配恰好两个<,同时完整捕获标记词,可以用以下正则:
^\s*cat.*?(?<!<)<{2}(?!<)\s*(.*)
各部分解释
^\s*cat:匹配行首可选空格后的cat关键字.*?:非贪婪匹配,避免吃掉后面的<,直到找到符合条件的<<(?<!<)<{2}(?!<):(?<!<):负向回顾后发断言,确保<<前面不是<,避免匹配<<<中的后两个<<{2}:匹配恰好两个<(?!<):负向先行断言,确保<<后面不是<,避免匹配<<<的情况
\s*:匹配<<和标记词之间的可选空格(.*):捕获标记词(如果需要限制标记词格式,比如只能是大写字母,可以改成([A-Z]+))
测试验证
- 对于
cat <<EOF:会完整捕获EOF - 对于
cat << EOF:会完整捕获EOF - 对于
cat <<<EOF:不会匹配,因为存在三个< - 对于
cat x<<EOF:也能正确匹配并捕获EOF
内容的提问来源于stack exchange,提问作者mortenbo
相关产品推荐
相关产品推荐

