1

I an trying to automatically update some files. I need to find a line and delete some lines after that one; I also need to put a replacement line in place of the deleted lines.

An example will be more clear.

original:

line aa/aa/aa/aa
line bb/bb/bb/bb
line cc/cc/cc/cc
line dd/dd/dd/dd
line ee/ee/ee/ee
line ff/ff/ff/ff
line gg/gg/gg/gg
line hh/hh/hh/hh
line ii/ii/ii/ii

so I want to:

  1. search a pattern, like bb/bb/bb/bb, so we kept the first two lines; then

  2. remove and replace the 3rd through 6th next lines with uu/uu/uu/uu

final result:

line aa/aa/aa/aa
line bb/bb/bb/bb
line cc/cc/cc/cc
line dd/dd/dd/dd
line uu/uu/uu/uu
line ii/ii/ii/ii

I can find and remove the lines with:

sed -e '/pattern/{n;N;N;N;N;d}' file.txt

but I don't know how to add in my new line at the same time. I also have trouble with patterns containing some /.

  • You’ll have a much easier time using ex for this. I don’t have time to write the answer now, but I will try to later. – Wildcard Jun 14 at 21:44
1

As Wildcard hinted at, a scriptable editor makes this easier. To replace the third through sixth lines after the pattern bb/bb/bb/bb with the single line line uu/uu/uu/uu, you could use the following ed pipeline:

printf '%s\n' '/bb\/bb\/bb\/bb' '+3,+6c' 'line uu/uu/uu/uu' '.' 'wq' | ed -s file.txt > /dev/null

At a high level, we're sending newline-separated commands through a pipeline to ed, of which we are instructing to silently edit the file named file.txt and to drop any "standard output" into the bit-bucket /dev/null. The -s flag suppresses the normal output from ed of the number of bytes read in and the number of bytes written out. ed will also print matched lines (the "bb" one in this scenario), so that's dropped via the > /dev/null. We could have dropped the -s flag and redirected both stdout and stderr.

The ed commands themselves are:

  • /bb\/bb\/bb\/bb -- search for the line containing /bb/bb/bb/bb, where we have to escape the forward slashes because otherwise ed thinks that we're trying to send a strange bb/bb/bb command to the line matching /bb/, due to the forard-slash being a sytnatical character.
  • +3,+6c -- from this line, change the 3rd through 6th lines after it ...
  • line uu/uu/uu/uu -- to this line
  • . -- and tell ed that we're done replacing lines
  • wq -- then write the file to disk and quit ed

Note that this assumes the bb/bb/bb/bb appears in the file; otherwise, you'll end up with an error as you try to tell ed to edit lines three through six after the end of the file. If you can't guarantee that the pattern exists in the file, you might precede this script with a grep -q bb/bb/bb/bb file.txt && printf... to conditionally execute the pipeline.

  • ed -s - <<EOF should work too (it may be -S) – D. Ben Knoble Jun 15 at 5:58
0

With GNU sed:

sed -ne '\#bb/bb/bb/bb#{p;n;p;n;p;n;n;n;n;a line uu/uu/uu/uu' -e ';b };p' file.txt 

Output:

line aa/aa/aa/aa
line bb/bb/bb/bb
line cc/cc/cc/cc
line dd/dd/dd/dd
line uu/uu/uu/uu
line ii/ii/ii/ii

To save the output to file.txt change -ne to -ine.


Note: the gap in the code with the two -e switches is because the gap tells the append command where to stop appending.

Another method is to use POSIX sed, but it's not a one-liner:

sed -n '\#bb/bb/bb/bb#{p;n;p;n;p;n;n;n;n;a\
line uu/uu/uu/uu
b };p' file.txt
0

With POSIX sed, you may do as follows:

 $ sed -e '
   \:line bb/bb/bb/bb:!b
    n;n;n
    N;N;N
    s:.*:line uu/uu/uu/uu:
 ' inp

After we're done printing the target line and two lines after it, we accumulate the next three lines in the pattern space and change the whole of the pattern space with the intended line. The newline is taken care of by sed itself when taking out the pattern space to the stdout.

Note we are assuming there are sufficient lines after the target line so that N does not fail.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.