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

Dev*_*lar 1 sed

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

---
foo: bar
bar: baz
---
baz: quz
---
Some text
Some text
Some text
Run Code Online (Sandbox Code Playgroud)

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

Win*_*ute 5

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

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

这样做

: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 ---
Run Code Online (Sandbox Code Playgroud)

@ mklement0指出\|只有GNU才能使用sed.一种解决这个问题的方法,因为\|只需要捕获---第一行,就可以了

sed ':a $!{ N; ba; }; s/^/\n/; s/\n---\n/\n/2; s/^\n//' filename
Run Code Online (Sandbox Code Playgroud)

这样做:

: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.
Run Code Online (Sandbox Code Playgroud)

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

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

sed '/^---$/ { x; s/.*/&_/; /^__$/ { x; d; }; x; }' filename
Run Code Online (Sandbox Code Playgroud)

那是:

/^---$/ {    # 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.
}
Run Code Online (Sandbox Code Playgroud)

...或者只是使用awk:

awk '!/^---$/ || ++ctr != 2' filename
Run Code Online (Sandbox Code Playgroud)