为什么这个 sed 命令不替换倒数第三个“and”?

为什么这个 sed 命令不替换倒数第三个“and”?

更新 2020 年 5 月 26 日

看来这是一个错误,所以我提交了一个错误。它的 ID 是#41558。


我只是在乱搞sed,我想出了这个练习:替换倒数第三个出现的“and”(这个词,不是子字符串),以创建:

dog XYZ foo and bar and baz land good

我以为这会起作用

echo 'dog and foo and bar and baz land good' |
    sed -E 's/(.*)\band\b((.*\band\b){2})/\1XYZ\2/'

但它实际上取代了倒数第二个出现的“and”。我能想到的唯一解释是它包含“土地”作为其中之一\band\b,但情况不应该是这样,因为我包含了“\b边界”一词?

答案1

这很难做到,因为sed不支持环视等(正如您可以在 PCRE 中所做的那样)。反转字符串并替换从头开始第三次出现的反转单词,然后再次反转会更容易。

$ echo 'dog and foo and bar and baz land good' | rev | sed 's/\<dna\>/XXX/3' | rev
dog XXX foo and bar and baz land good

至于为什么你的表达不起作用,这看起来像是一个错误。反向引用\3似乎是 string  baz land,就好像\bbefore andin.*\band\b从未产生过任何效果。

命令

sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'

sed似乎在 OpenBSD 上用它的原生(使用\<\>代替\b)做了正确的事情。

sed我还没有找到针对 GNU或 GNU 的现有错误报告glibc,尽管如果它至少是这样的话我也不会感到惊讶有关的glibc 错误 25322(因为,见下文)。

您可以通过更详细的方式来解决这个问题:

sed -E 's/(.*)\band\b(.*\band\b.*\band\b)/\1XYZ\2/'

答案2

我建议提出问题。我已经测试了这些示例,这会导致GNU grep,GNU sed和产生相同的行为GNU awk。除了下面提到的一种情况。

  • 错误的输出:

    $ echo 'cocoa' | sed -nE '/(\bco){2}/p'
    cocoa
    

    sed -nE '/(\<co){2}/p'并且awk '/(\<co){2}/'也有错误的行为,但grep -E '(\<co){2}'正确地没有给出输出

  • 行为正确,没有输出:

    $ echo 'cocoa' | sed -nE '/\bco\bco/p'
    
  • it输出错误:后面只有 1 个完整单词with

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
    it line XYZ too
    
  • 行为正确,输入未修改

    $ echo 'it line with it here sit too' | sed -E 's/with.*\bit\b.*\bit\b/XYZ/'
    it line with it here sit too
    
  • 将单词边界更改为\<\>会导致不同的问题。

    这正确不修改输入:

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here sit too
    

    这正确修改了输入

    $ echo 'it line with it here it too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line XYZ too
    

    但是这个无法修改输入

    $ echo 'it line with it here it too sit' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here it too sit
    

此外,只有当冲突的单词开头有额外的字符时,才会出现有问题的行为。例如,itsit。但如果末尾有字符则不然。例如,itsiteitem

$ echo 'it line with it here item too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
it line with it here item too
$ echo 'it line with it here it too item' | sed -E 's/with(.*\<it\>){2}/XYZ/'
it line XYZ too item

相关内容