我在任何地方都找不到这个问题的答案。这听起来很简单,但我开始认为也许并非如此。
我希望 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
文本并打印出最后一行。