I need to write a simple script to replace a block of text in a configuration file with the contents of another file.
Let\'s assume with have the following simplifie
How about this little snippet I created:
sed -n \
-e "1,/<\!-- BEGIN realm -->/ p" \
-e"/<\!-- END realm -->/,$ p" \
-e "/<\!-- BEGIN realm -->/ r realm.xml" \
server.xml
The first commands prints the lines up to <!- BEGIN realm -->
the second command prints the line starting at <!-- END realm -->
and the third commands append the text in the file 'realm.xml'. If only I could simplify the removing of the lines between <!- BEGIN realm -->
and <!-- END realm -->
without removing the marker lines it would as simple as it gets. And it can be done inplace
with sed!!!
I ran into this same need (hence finding this question). After messing around with sed and awk for far too long, I eventually realised there's nothing wrong with using a modern, readable, understandable, widely available language like Python:
python <<EOF
import os, sys, re
fname = 'server.xml'
os.rename(fname, fname + '.orig')
with open(fname + '.orig', 'r') as fin, open(fname, 'w') as fout:
data = fin.read()
data = re.sub(r'(<!-- BEGIN realm -->).*?(<!-- END realm -->)',
r'\1\n' +
'insert whatever you want here\n' +
r'\2\n', data, flags=re.DOTALL)
fout.write(data)
EOF
I think sed and awk have had their day. They were useful once upon a time, but very few people can read or write either without documentary assistance these days.
(Source: the internet)
you can use awk
awk 'FNR==NR{ _[++d]=$0;next}
/BEGIN realm/{
print
for(i=1;i<=d;i++){ print _[i] }
f=1;next
}
/END realm/{f=0}!f' realm.xml server.xml > temp && mv temp server.xml
realm.xml is passed to awk as the first file. FNR==NR means getting the records of the first file passed in and store to variable _
. awk will process the next file once FNR!=NR. if awk finds /BEGIN realm/
, print the BEGIN realm
line, then print what is stored in _
. By setting a flag (f) to 1, the rest of the lines after BEGIN realm
will not be printed until /END realm/
is detected.
Give this a try:
sed -i -ne '/<!-- BEGIN realm -->/ {p; r realm.xml' -e ':a; n; /<!-- END realm -->/ {p; b}; ba}; p' server.xml
You may also use the ed command (cf. http://wiki.bash-hackers.org/howto/edit-ed ):
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s server.xml
H
/BEGIN realm/i
.
/BEGIN realm/+1,/END realm/-1d
.-1r realm.xml
wq
EOF
I was unable to get Dennis solution easily working on OS X (its BSD sed is slightly different). I found this other solution that I was able to make work on both Linux and OS X (I have a mixed environment). The original version on superuser.com works only on Linux, here I fixed it:
lead='^<!-- BEGIN realm -->$'
tail='^<!-- END realm -->'
sed -e '/'"$lead"'/,/'"$tail"'/{ /'"$lead"'/{p; r realm.xml' -e' }; /'"$tail"'/p; d;} ' server.xml
Here a version of Dennis code that works also on OS X (using multiple lines):
sed -ne '/'"$lead"'/ {
p
r realm.xml
:a
n
/'"$tail"'/ {
p
b
}
ba
}
p' server.xml
Both these codes print the output on stdout. Use redirection or, to substitute the file inline, add the option '-i' (on linux) or '-i ""' (on BSD/OS X).