sed:在同一行内,一旦到达某个字符串就停止重复模式替换

sed:在同一行内,一旦到达某个字符串就停止重复模式替换

我在任何地方都找不到这个问题的答案。这听起来很简单,但我开始认为也许并非如此。

我希望 sed 删除此字符串中 STOP 之前的所有 CAT:

two CAT two four CAT CAT seven one STOP four CAT two CAT three

所以我希望的输出将是:

two two four seven one STOP four CAT CAT two CAT three

字符串中的任何位置都可以有任意数量的 CAT。停止标记也可以位于任何地方,但只能是其中之一,并且始终拼写为 STOP。

(编辑:正如下面指出的,我的问题是不明确的 - CAT 必须有相邻的空格还是可以有任何字符作为边界?也许只有非字母数字字符可以?展示我的实际用例很激烈(一个大的 bash 函数),所以我简化了,太多了,请读者记住,下面的解决方案可能会对邻接做出不同的假设,谢谢)

答案1

您可以在循环中一次替换一个,直到CAT之前不再有 s STOP

$ echo 'two CAT two four CAT CAT seven one STOP four CAT two CAT three' |
    sed -e :a -e '/CAT.*STOP/s/CAT //;ta'
two two four seven one STOP four CAT two CAT three

答案2

对于任何 awk:

awk '{while($0~/CAT .*STOP/)sub(/CAT /,"")}1' file
$ echo 'two CAT two four CAT CAT seven one STOP four CAT two CAT three' |
  awk '{while($0~/CAT .*STOP/)sub(/CAT /,"")}1'
two two four seven one STOP four CAT two CAT three

答案3

perl

perl -pe 's/CAT (?=.*STOP)//g'

CAT仅当STOP该行后面存在时才会替换

答案4

(如果这比 sed 难题更重要,我强烈建议根本不要使用 sed 来完成它。您可以用 python 轻松且非常清晰地编写此内容,与此处晦涩难懂的答案不同。)

您可以在 sed 中使用循环,如下所示。代码下方的说明和注意事项。

s/STOP/@/
: loop
s/^\([^@]*\)CAT\(.*\)$/\1\2/
t loop
s/@/STOP/
p

运行它作为

$ sed -n -f t.sed

它会按照您的描述修复每一行。请注意,如果不STOP存在,则编写的代码CAT将从输入行中删除所有出现的 。此外,此代码假定这@不会出现在您的输入行中。如果是这样,您将需要找到另一个标记字符。


那么,这是怎么回事?让我们看一下代码:

s/STOP/@/

编写一个匹配某些内容不存在的正则表达式是很困难的sed,除了单个字符的情况,例如x,在这种情况下[^x]*可以完成这项工作。因此,将我们的哨兵替换STOP为我们知道在该行的其余部分中未使用的单个字符。如果没有这样的性格,生活就会变得更加困难,然后我们就会记得这sed确实不是适合这项工作的工具。

: loop
s/^\([^@]*\)CAT\(.*\)$/\1\2/
t loop

这是关键部分。: loop在 sed 脚本中声明一个标签,您可以稍后分支回来。接下来,在该s///行中,尝试找到CAT前面没有的@标记并替换它,保留之前和之后的文本。如果发生替换,t loop将返回并重loop试。如果替换失败,即如果没有找到替换,则不采取CAT返回的分支。loop

s/@/STOP/
p

恢复实际STOP文本并打印出最后一行。

相关内容