开发者

Replace delimited block of text in file with the contents of another file

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 simplified files:

server.xml

<?xml version='1.0' encoding='U开发者_JS百科TF-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="80" protocol="HTTP/1.1"/>
    <Engine name="Catalina" defaultHost="localhost">
      <!-- BEGIN realm -->
        <sometags/>
        <sometags/>
      <!-- END realm -->
      <Host name="localhost" appBase="webapps"/>
    </Engine>
  </Service>
</Server>

realm.xml

<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
       resourceName="UserDatabase"/>

I want to run a script and have realm.xml replace the contents between the <!-- BEGIN realm --> and <!-- END realm --> lines. If realm.xml changes then whenever the script is run again it will replace the lines again with the new contents of realm.xml. This is intended to be run in /etc/init.d/tomcat on startup of the service on multiple installations on which the realm is going to be different.

I'm not so sure how can I do this simply with awk or sed.


Give this a try:

sed -i -ne '/<!-- BEGIN realm -->/ {p; r realm.xml' -e ':a; n; /<!-- END realm -->/ {p; b}; ba}; p' server.xml


TOTAL_LINES=`cat server.xml | wc -l`
BEGIN_LINE=`grep -n -e '<!-- BEGIN realm -->' server.xml | cut -d : -f 1`
END_LINE=`grep -n -e '<!-- END realm -->' server.xml | cut -d : -f 1`
TAIL_LINES=$(($TOTAL_LINES-$END_LINE))

head -n $BEGIN_LINE server.xml > server2.xml
cat realm.xml > server2.xml
tail -n $TAIL_LINES server.xml > server2.xml

(OK, this does not use awk or sed... I assumed that was not an exclusive requirement :-)


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.


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!!!


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 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)


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).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜