Kriss Kriss - 27 days ago 8
Linux Question

Replace text between two strings in file using linux bash

i have file "acl.txt"

192.168.0.1
192.168.4.5
#start_exceptions
192.168.3.34
192.168.6.78
#end_exceptions
192.168.5.55


and another file "exceptions"

192.168.88.88
192.168.76.6


I need to replace everything between #start_exceptions and #end_exceptions with content of exceptions file. I have tried many solutions from this forum but none of them works.

Answer Source

EDITED:

Ok, if you want to retain the #start and #stop, I will revert to awk:

awk '
    BEGIN       {p=1}
    /^#start/   {print;system("cat exceptions");p=0}
    /^#end/     {p=1}
    p' acl.txt

Thanks to @fedorqui for tweaks in comments below.

Output:

192.168.0.1
192.168.4.5
#start_exceptions
192.168.88.88
192.168.76.6
#end_exceptions
192.168.5.55

p is a flag that says whether or not to print lines. It starts at the beginning as 1, so all lines are printed till I find a line starting with #start. Then I cat the contents of the exceptions file and stop printing lines till I find a line starting with #end, at which point I set the p flag back to 1 so remaining lines get printed.

If you want output to a file, add "> newfile" to the very end of the command like this:

awk '
    BEGIN       {p=1}
    /^#start/   {print;system("cat exceptions");p=0}
    /^#end/     {p=1}
    p' acl.txt > newfile

YET ANOTHER VERSION IF YOU REALLY WANT TO USE SED

If you really, really want to do it with sed, you can use nested address spaces, firstly to select the lines between #start_exceptions and #end_exceptions, then again to select the first line within that and also lines other than the #end_exceptions line:

sed '
   /^#start/,/^#end/{
      /^#start/{
         n
         r exceptions
      }
      /^#end/!d
   }
' acl.txt

Output:

192.168.0.1
192.168.4.5
#start_exceptions
192.168.88.88
192.168.76.6
#end_exceptions
192.168.5.55

ORIGINAL ANSWER

I think this will work:

sed -e '/^#end/r exceptions' -e '/^#start/,/^#end/d' acl.txt

When it finds /^#end/ it reads in the exceptions file. And it also deletes everything between /#start/ and /#end/.

I have left the matching slightly "loose" for clarity of expressing the technique.