sed:如何删除文件中的第二个匹配项

时间:2015-02-28 18:32:12

标签: sed

我有一个看起来像这样的文件(伪代码):

---
foo: bar
bar: baz
---
baz: quz
---
Some text
Some text
Some text

我需要删除第二个 ---行,并且只删除它。我知道sed可以做到这一点,但我从来没有能够从我能找到的任何sed文档中做出头脑或尾巴......

5 个答案:

答案 0 :(得分:5)

使用sed最简单的方法是首先将整个文件读入模式空间并进行处理:

sed ':a $!{N; ba}; s/\(^\|\n\)---\n/\n/2' filename

这样做

:a                       # jump label for looping
$!{                      # if the end of input is not reached
  N                      # fetch the next line, append it to the pattern space
  ba                     # go back to :a
}                        # after this, the whole file is in the pattern space.
s/\(^\|\n\)---\n/\n/2    # then: remove the second occurrence of a line that
                         # consists only of ---

@ mklement0指出\|仅适用于GNU sed。解决这个问题的一种方法,因为\|只需要在第一行中捕获---,所以

sed ':a $!{ N; ba; }; s/^/\n/; s/\n---\n/\n/2; s/^\n//' filename

这样做:

:a $!{ N; ba; }  # read file into the pattern space
s/^/\n/          # insert a newline before the first line
s/\n---\n/\n/2   # replace the second occurrence of \n---\n with \n
s/\n//           # remove the newline we put in at the beginning.

这样,第一行不再是特例。

如果不将整个文件读入缓冲区,则必须使用字符构建计数器:

sed '/^---$/ { x; s/.*/&_/; /^__$/ { x; d; }; x; }' filename

那是:

/^---$/ {    # if a line is ---
  x          # exchange pattern space and hold buffer
  s/.*/&_/   # append a _ to what was the hold buffer
  /^__$/ {   # if there are exactly two in them
    x        # swap back
    d        # delete the line
  }
  x          # otherwise just swap back.
}

...或者只是使用awk:

awk '!/^---$/ || ++ctr != 2' filename

答案 1 :(得分:2)

这里有一些意大利面条sed代码(使用goto

sed '/^---/ {:a;n;/^---/{d;bb};ba;:b}' file

带评论

sed '/^---/ {      # at the first match
    :a             # label "a"
    n              # get the next line of input
    /^---/{d;bb}   # if it matches, delete the line and goto "b"
    ba             # branch to "a" (goto)
    :b             # label "b"
}' file

但是我会添加我的观点,即对任何复杂的事物使用sed会导致无法维护的代码。使用awk或perl。感谢有机会炫耀;)

答案 2 :(得分:1)

sed用于单行上的简单替换。对于其他任何你应该使用awk:

$ awk '!(/^---$/ && ++cnt==2)' file
---
foo: bar
bar: baz
baz: quz
---
Some text
Some text
Some text

答案 3 :(得分:1)

这可能对您有用(GNU sed):

sed '/^---/{x;s/^/n/;/^n\{2\}$/{x;d};x}' file

在容纳空间中放置一个计数器。每次遇到以---开头的行时,请向计数器添加一个,如果计数器为2,则删除当前行。

答案 4 :(得分:0)

请参阅Sed replace every nth occurrence

解决方案使用awk而不是sed,但是"使用正确的工具来完成工作"。在sed中可能会或者可能不会这样做,但即使是这样,在像awk或perl这样的工具中也会容易得多。