【问题标题】:How to match between a start-delimiter and end-delimiter such that the text does not contain the end-delimiter (i.e. non-greedy match)?如何在开始分隔符和结束分隔符之间进行匹配,以使文本不包含结束分隔符(即非贪婪匹配)?
【发布时间】:2016-11-05 07:42:14
【问题描述】:

在 POSIX shell 脚本中,我需要找到所有出现在 {{}} 中的文本,并将文本以及周围的大括号替换为星号。

例如,如果输入是

{{ abc }} def {{ ghi {jkl} mno }} pqr

那么输出一定是

* def * pqr

我还没有想出一个有效的sed 命令。

我尝试了几个命令,但它们不起作用。例如,以下命令不会产生所需的输出,因为sed 会进行贪婪匹配。它最终匹配 {{ abc }} def {{ ghi {jkl} mno }} 作为第一个匹配项,而不仅仅是 {{ abc }}

$ echo "{{ abc }} def {{ ghi {jkl} mno }} pqr" | sed 's/{{.*}}/*/g'
* pqr

这是另一个不起作用的示例,因为它最终匹配的太少。它不匹配{{ ghi {jkl} mno }}(我们想要匹配),因为这部分字符串包含}

$ echo "{{ abc }} def {{ ghi {jkl} mno }} pqr" | sed 's/{{[^}]*}}/*/g'
* def {{ ghi {jkl} mno }} pqr

我还能怎么做这样的比赛?

我已经使用了Non greedy regex matching in sed?,但那里的解决方案没有帮助,因为在这里我想匹配{{}} 之间的所有内容,除了两个连续字符的特定序列,即}}。如果我尝试匹配分隔符之间除了单个字符之外的所有内容,那么该问题的答案会有所帮助。

【问题讨论】:

    标签: regex shell posix


    【解决方案1】:

    如果您有一个正则表达式匹配不包含"}}" 的内容,那么您可以将其用作"{{" exp "}}"。不幸的是sed 没有补码正则表达式运算符。许多正则表达式实现都这样做,因为正则语言的补码是正则的。所以我们确实知道它存在,但我们只需要手动构建它。

    在比 sed 更易读的格式中,接近的是 "{{" ( [^}]* ( "}" [^}] )? )* "}}"

    在正确的sed 中是:

    $ echo "{{ abc }} def {{ ghi {jkl} mno }} pqr" \
        | sed 's/{{\([^}]*\(}[^}]\)\?\)*}}/*/g'
    * def * pqr
    $
    

    这可能不是您想要的,这取决于您是否期望连续三个大括号。这个abc {{ def { ghi }}} 会发生什么?如果您确实需要平衡大括号,这将使其脱离常规语言领域并进入需要更强大工具的上下文无关语言。

    根据您的用户名,您可能想阅读一本关于形式语言和自动机理论的书。它可能是“旧”技术,但它非常强大,并且整天被各种技术使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-11
      • 1970-01-01
      相关资源
      最近更新 更多