SED替换了模式的首次出现(和范围)

时间:2019-06-17 08:56:25

标签: sed replace range find-occurrences

在这种情况下,使用SED(与sed -r 's/[^[:space:]]*/TEST/4g'相反)可以更改字符串的前4(或更多)次出现:

TEST TEST TEST TEST five six seven

我使用AWK两次按顺序处理单词反转顺序,但这很长很复杂,我只想用SED就能做到这一点:

echo one two three four five six seven | awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}'  | sed -r 's/[^ ]*/TEST/4g' |  awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}'

也许还有选择更改发生范围的选项,例如3-5、6-12,...?

示例输入为:

  

一二三四五五六七

     

八点九十一点十一十二十三点十三十四点

     

十五十六十七十七十八十九二十一十二

4 个答案:

答案 0 :(得分:3)

单个 AWK

awk '{for(i=1;i<=NF;i++) if(i<5){$i="TEST"}; print}'

试运行:

$ echo one two three four five six seven | awk '{for(i=1;i<=NF;i++) if(i<5){$i="TEST"}; print}'
TEST TEST TEST TEST five six seven

此解决方案简短,可读且可维护。如果您不满意,请添加一些有关您的特定问题的详细信息。


Perl 等效解决方案:

perl -pe 's/\S+/$i++<4?"TEST":$&/ge'

试运行:

$ echo one two three four five six seven | perl -pe 's/\S+/$i++<4?"TEST":$&/ge'
TEST TEST TEST TEST five six seven

  

也许可以选择更改发生范围,例如3-5、6-12

AWK:

awk '{for(i=3;i<6;i++)$i="TEST";print}'

在新提供的输入文件上运行测试:

$ awk '{for(i=3;i<6;i++)$i="TEST";print}' input
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one

Perl:

perl -pe 's/\S+/++$c~~[3..5]?"TEST":$&/ge'

在新提供的输入文件上运行测试:

$ perl -pe '$c=0;s/\S+/++$c~~[3..5]?"TEST":$&/ge' input
Smartmatch is experimental at -e line 1. <== This is a warning that goes to STDERR
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one

答案 1 :(得分:1)

heremikeserv提供了答案。 注意:如果要处理一个范围,则需要使用最大范围,因为它会处理尽可能多的匹配项而不会引发任何异常/错误。

GNU sed:

echo 'one two three four five six seven' | \
  sed 's/[^[:space:]]*/\n&/g;:t;/\n/{x;/.\{4\}/!{s/$/./;x;s/\n[^[:space:]]*/TEST/;bt};x};s/\n//g'

POSIX sed:

nl='
';
echo 'one two three four five six seven' | sed "s/[^[:space:]]*/\\$nl&/g;:t${nl}/\n/{x;/.\{4\}/!{${nl}s/$/./;x;s/\n[^[:space:]]*/TEST/;bt$nl};x$nl};s/\n//g"

请参见online sed demo

原始说明(请注意,1替换为2,您可以使用任何其他模式):

  

我在这里使用了两种著名的技术。首先,每个   1在行中出现\n1。这样,就像我   接下来执行递归替换,我可以确定不替换   两次出现 if 我的替换字符串包含我的替换   串。例如,如果我将he替换为hey,它将仍然有效。

     

我这样做:

s/1/\
&/g
     

其次,我通过将一个字符添加到   每次出现h个旧空间。一旦达到三个,就不会再发生了。如果   您将其应用于数据并将\{3\}更改为总计   您想要的替代品,/\n1/可以满足您的要求   要替换,您应该只替换任意数量的

答案 2 :(得分:0)

这对于sed来说是完全不合适的任务,因为sed就是对单个字符串仅此做简单的s/old/new/。在每个UNIX框上的任何外壳中都有任何awk:

$ echo one two three four five six seven | awk '{for (i=1; i<=4; i++) $i="TEST"}1'
TEST TEST TEST TEST five six seven

$ echo one two three four five six seven | awk '{for (i=3; i<=5; i++) $i="TEST"}1'
one two TEST TEST TEST six seven

,如果需要对其进行参数化:

echo one two three four five six seven |
    awk -v beg=3 -v end=5 '{for (i=beg; i<=end; i++) $i="TEST"}1'
one two TEST TEST TEST six seven

答案 3 :(得分:0)

$ echo "one two three four fix six" | \
sed -E ':r s/(^|(TEST )+)[^ ]*/\1TEST/;/^(TEST ){4}/!br'
TEST TEST TEST TEST fix six

说明:

  • :r标签为r的分支回到
  • s/(^|(TEST )+)[^ ]*/\1TEST/;替换项,用于替换只出现一个非TEST单词,以行的开头或1个或多个TEST开头
  • /^(TEST ){4}/!br'正则表达式查找所需内容,然后在!br尚未匹配的情况下分支回到:r

显然,这很脆弱。如果任何行没有四个单词,它将无限循环。只能是GNU sed。