1

I need to add a the multi line string in TEXT into myfile.txt where it last find the text # My Search.

If it was a regular pattern replace then sed with i would do the trick. However I don't know how to go 2 lines above the match in sed.

Example, myfile.txt:

text1
text2
#
# My Search
#
text4
text5
#
# My Search
#
text6

TEXT looks like:

TEXT="
[my search]
home=/var/home
string=random

"

myfile.txt should look at the end:

text1
text2
#
# My Search
#
text4
text5

[my search]
home=/var/home
string=random

#
# My Search
#
text6

4 Answers 4

2

Use grep -n to extract the line number, then use it in a sed address to insert the text, which needs to be properly quoted, i.e. there must be a backslash before each newline:

line=$(grep -n '# My Search' myfile.txt | tail -n1 | cut -f1 -d:)
((--line))
text=${TEXT//$'\n'/$'\\\n'}
text=${text%$'\\\n'}$'\n'

sed "$line i \\
$text" myfile.txt
0
0

tac + bash (variable substitution) + GNU sed approach:

txt_reversed=$(echo "$TEXT" | tac)
tac myfile.txt \
| sed -e "N;N;N; s~#\n# My Search\n#~&${txt_reversed//$'\n'/\\n}\n~" | tac

The output:

text1
text2
#
# My Search
#
text4
text5

[my search]
home=/var/home
string=random

#
# My Search
#
text6
0

The easiest way to do this in one go is to use a tool that can search backward, ed is one of them. Ideally you would have the text saved in a file (e.g. insert.txt) and insert the file content two lines before the last occurrence of PATTERN so you could run:

ed -s infile <<\IN
.t.
?PATTERN?-2r insert.txt
$d
,p
q
IN

It finds the last occurrence of PATTERN, goes up another 2 lines and reads in insert.txt. It then prints the content of the buffer via ,p. Replace that with w if you want to edit the file in-place.


With other tools that can't search backward a two-pass is the way to go: 1st pass - get the line number and 2nd pass - insert the text content after line NR-2 or before line NR-1. For text saved in a variable I favor awk over other tools that rely on regex (the latter require pre-processing the text to be inserted so as to escape any special characters: there are several ones that need escaping, not just the newline, depending on the technique used):

export TEXT
awk 'NR==FNR{if ($0 ~ /PATTERN/){c=NR};next}
{if (FNR==(c-1)){print ENVIRON["TEXT"]}};1' infile infile
0

My first contribution in stackExchange.

This is how to insert a line with sed before last match.

sed -e '/PATTERN[^\n]*/,$!b;//{x;//p;g};//!H;$!d;x;iTEXTOINSERT' FILE

This is how to insert a line with sed before first match.

sed -i -e '/PATERN/{a\\TEXTOINSERT' -e ':a;n;ba}' FILE

Regards!

1
  • 1
    But does it add the text 2 lines before the last pattern match? Commented Mar 8, 2021 at 0:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .