如何使用 sed 在关键字第 n 次出现后添加一行?

如何使用 sed 在关键字第 n 次出现后添加一行?

使用 sed,我想在第 n 次出现后添加检查

输入:

DCR
DCR
DCR

输出:

DCR
DCR
check
DCR

可以使用sed吗?

答案1

使用 GNU sed,您可以替换na 中的第 th 模式线

$ echo "foofoofoofoo" | sed 's/foo/&\nbar/2'
foofoo
barfoofoo

但对于n包含模式的第 3 行,awk 更容易:

awk -v n=2 -v patt=foo '{print} $0 ~ patt && ++count == n {print "bar"}' <<END
foo1
foo2
foo3
foo4
END
foo1
foo2
bar
foo3
foo4

答案2

使用 GNU sed:

sed -z 's/DCR/&\ncheck/2' <input >output

对于非更新版本:

sed '/DCR/{p;s/.*/1/;H;g;/^\(\n1\)\{2\}$/s//check/p;d}' <input >output

DCR如果一行中出现超过 1 次:

sed '
/DCR/{p
      x                               # tests if already have met pattern
      /^\(\n\a\)\{2\}/!{              #+apropriate times and, if so, cancel
        x                             #+the rest of commands
        s/DCR/\a/g                    # exchange DCR by \a symbol
        s/^[^\a]*\|[^\a]*$//g         # delete everything before & after it  
        s/[^\a]\+/\n/g                # substitute everything between by \n
        H
        g
        /^\(\n\a\)\{2\}/s/.*/check/p} # add 'check' for double pattern
      d}' <input >output

答案3

您可以sed在堆栈上执行此操作...

sed '/match$/N
     s/\n/&INSERT&/3;t
     $n;N;P;D'

那会插入INSERT每 3 个之后非顺序的的发生match在输入中。这是我知道的最有效的方法,sed因为它不会尝试存储不同之间发生的所有行matches,也不需要缓冲区交换或反向引用比较,而是简单地增加 的sed唯一计数方法 - 通过其行周期来增加其行号。

当然,这会增加一些开销 - 每个匹配模式空间都会变得更大一些 - 但它仍然是相同的流,并且没有回溯。只是先进先出- 我认为,这是一种非常适合的方法sed。事实上,与其回去检查匹配,sed还不如进一步前进每场比赛。我有点小得意,不知道为什么以前从来没有想到过。

不过,上面的版本会在某种程度上压缩重复,因为它只能在输入后面一行工作。解决方案是进一步推进,并且只需要一点额外的复杂性,即在环路内部设置b牧场:label 短路环路N;P;D以保持电流。

它的工作原理如下:

seq 100000| sed -ne':n
            s/\n/&\tCheck&\t/5p;t
            N;/0000$/bn'  -eD

...对我来说,打印...

49995
49996
49997
49998
49999
    Check
    50000
99995
99996
99997
99998
99999
    Check
    100000

您会看到,为了维持计数,它会在每次出现时增加其行缓冲区match并将另一条线钉到图案空间上的滑动窗口上。这样,验证是否已找到匹配所需的就是尝试替换掉s///nth \n模式空间中的 ewline 字符。如果可以的话,我们遇到过n matches到目前为止,test 可以将我们从当前迭代中分支出来并完全清除增量。

在上面的示例中,对于每个以字符串结尾的模式空间,缓冲区都会递增一次0000。当找到其中 5 个时,sed打印当前模式空间 - 及其整个缓冲区 - 并清除计数器。

为了你的事:

printf DCR\\n| tee - - - - - |
sed -e:n -e's/\n/&\tCheck&\t/2;t
     $n;    N;/DCR$/bn' -eP\;D

DCR
DCR
    Check
    DCR
DCR
DCR
    Check
    DCR

现在,如果你想标记仅有的nth发生,也很简单:

printf DCR\\n        |
tee - - - - - - - - -|
sed -e:n -e's/\n/&\tCheck&\t/3;te
     $n;  N;/DCR$/bn' -e'P;D;:e
     n;be'

...如果您仔细观察,您可能会发现我们仅仅触及了表面...

DCR
DCR
DCR
    Check
    DCR
DCR
DCR
DCR
DCR
DCR
DCR

答案4

GNU sed

sed不太适合这项任务,但当然你仍然可以做到。这是n在保留空间中保存很长的字符串的一种方法,并使用它来计算DCR出现的次数:

n=2

((yes | head -n$n | tr -d \\n; echo); cat infile) | 
sed '
  1 {h;d}            # save counting string
  /DCR/ {            #
    x; s/.//; x      # n--
    T chk            # if n=0 goto "chk"
  }
  P;D 
  :chk               # insert check
  i\check
  :a; N; ba          # print rest of file
'

awk

正如所指出的格伦,awk 更干净,这是一个高尔夫版本,但类似的逻辑:

<infile awk '!n { print "check" } /DCR/ { n-- } 1' n=2

相关内容