Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sed: How to delete second match in a file

Tags:

sed

I have a file that's looking like this (pseudocode):

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

I need to delete the second --- row, and only that. I know that sed can do this, but I have never been able to make heads nor tails out of any sed documentation I could find...

like image 504
DevSolar Avatar asked Nov 05 '25 11:11

DevSolar


1 Answers

With sed the easiest way would be to first read the whole file into the pattern space and work on that:

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

This does

: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 points out that the \| only works with GNU sed. A way to work around that, since the \| is only necessary to catch --- in the first line, would be

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

This does:

: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.

This way, the first line is no longer a special case.

Without reading the whole file into a buffer, you'll have to construct a counter from characters:

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

That is:

/^---$/ {    # 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.
}

...or just use awk:

awk '!/^---$/ || ++ctr != 2' filename
like image 192
Wintermute Avatar answered Nov 09 '25 08:11

Wintermute



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!