Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

use sed to insert a line after matching a block of text

Tags:

bash

shell

sed

awk

I try to use sed to insert a line after [Block B] in the following file:

[Block A]  
line 1  
line 2  

[Block B]  
line 1  
line 2  

[Block C]  
line 1  
line 2  

The command I used:

sed '/\[Block B\]/,/^$/a\inserted line' file

The correct/desired result should be:

[Block B]  
line 1  
line 2  
inserted line  

However, I got this instead:

[Block B]  
inserted line  
line 1  
inserted line  
line 2  
inserted line  

Please tell me how I can get the desired result using sed. Thanks!

like image 653
tonytz Avatar asked Jun 25 '12 04:06

tonytz


2 Answers

sed -e '/\[Block B\]/{:a;n;/^$/!ba;i\inserted line' -e '}'
like image 118
Dennis Williamson Avatar answered Oct 02 '22 16:10

Dennis Williamson


I found this question while looking for a solution to my own problem, which was similar but a little different. I adapted the answers here to solve my problem.

I needed to insert some text at the end of a block inside a configuration file like this:

name1 {
    ...
}

name2 {
    ...
    inserted text line 1
    inserted text line 2
}

name3 {
    ....
}

To achieve this I took @toyntz comment from above and adapted it thus:

/^name2 {/,/^}/{
    /^}/i\    inserted text line 1
    /^}/i\    inserted text line 2
}

That is just the sed expression; it can be put in a file and executed with sed -f like this:

$ sed -f sed_expression data_file

This first expression searches for a range of lines starting with name2 { occurring at the beginning of a line and ending with } also occurring at the beginning of a line. That selects the block to work on. The remaining expression is enclosed in {curly braces} and operates on the selected range. It contains one command per line we wish to insert, each with an expression /^}/ that matches the line with the closing curly brace followed by an insert i operation to insert a line of text. The i is followed with a \ so that leading whitespace is also inserted.

I then took the expression a bit further, replacing the two insert commands with one:

/^name2 {/,/^}/{
    /^}/i\
    inserted text line 1\
    inserted text line 2
}

Here the text to be inserted by one command is spread across the following two lines. Note the additional trailing \ on the first line to continue the single command.

Next, I reduced it to one line. It makes it messy and harder to read but it still works:

/^name2 {/,/^}/{/^}/i\    inserted text line 1\n    inserted text line 2
}

The two lines to be inserted are separated by a newline \n. Astute readers will note that there are actually two lines there - you can't put the closing brace on the end of the first line; this is why the other answers above have a second -e expression. So, the above was the best I could do. To represent that on a bash command line:

sed -e '/^name2 {/,/^}/{/^}/i\    inserted text line 1\n    inserted text line 2' -e '}' data_file

I've written out this longhand in the hope that it explains to anyone looking to insert at the end of a block of text how a sed expression can be written to achieve that. Sed expressions can be quite cryptic and difficult to figure out - hopefully my explanations help in that regard.

like image 32
starfry Avatar answered Oct 02 '22 16:10

starfry