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
0

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

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

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.