用另一个文件的内容替换文件中的分隔文本块

时间:2010-04-23 15:11:17

标签: bash shell scripting sed awk

我需要编写一个简单的脚本,用另一个文件的内容替换配置文件中的文本块。

让我们假设有以下简化文件:

server.xml

<?xml version='1.0' encoding='UTF-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"/>

我想运行一个脚本并realm.xml替换<!-- BEGIN realm --><!-- END realm -->行之间的内容。如果realm.xml发生更改,则每当脚本再次运行时,它将再次使用realm.xml的新内容替换行。这个目的是在启动服务时在/etc/init.d/tomcat上运行,在多个安装上,域将是不同的。

我不确定如何使用awksed完成此操作。

7 个答案:

答案 0 :(得分:13)

尝试一下:

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

答案 1 :(得分:3)

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

(好吧,这不使用awk或sed ......我认为这不是一个独家要求: - )

答案 2 :(得分:2)

你可以使用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作为第一个文件传递给awk。 FNR == NR表示获取传入的第一个文件的记录并存储到变量_。一旦FNR!= NR,awk将处理下一个文件。如果awk找到/BEGIN realm/,请打印BEGIN realm行,然后打印_中存储的内容。通过将标志(f)设置为1,BEGIN realm之后的其余行将不会被打印,直到检测到/END realm/

答案 3 :(得分:1)

我创建的这个小片段怎么样:

sed -n \
  -e "1,/<\!-- BEGIN realm -->/ p" \
  -e"/<\!-- END realm -->/,$ p" \
  -e "/<\!-- BEGIN realm -->/ r realm.xml" \
  server.xml

第一个命令打印行到<!- BEGIN realm -->,第二个命令打印从<!-- END realm -->开始的行,第三个命令将文本附加到文件'realm.xml'中。如果我只是简化删除<!- BEGIN realm --><!-- END realm -->之间的行而不删除标记行,那么它就会变得简单。它可以用sed !!! {/ 1}完成inplace

答案 4 :(得分:1)

您也可以使用ed命令(参见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

答案 5 :(得分:0)

我遇到了同样的需求(因此找到了这个问题)。在用sed和awk搞砸了太久之后,我终于意识到使用像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

我认为sed和awk度过了他们的一天。它们曾经很有用,但是现在很少有人能够在没有文件帮助的情况下阅读或写作。

(来源:互联网)

答案 6 :(得分:0)

我无法轻易地在操作系统X上使用Dennis解决方案(其BSD sed略有不同)。我找到了另一个解决方案,我能够在Linux和OS X上工作(我有一个混合环境)。 superuser.com上的原始版本仅适用于Linux,我在这里修复了它:

lead='^<!-- BEGIN realm -->$'
tail='^<!-- END realm -->'
sed  -e '/'"$lead"'/,/'"$tail"'/{ /'"$lead"'/{p; r realm.xml' -e' }; /'"$tail"'/p; d;} '  server.xml

这里有一个版本的Dennis代码,它也适用于OS X(使用多行):

sed -ne '/'"$lead"'/ {
 p
 r realm.xml
 :a
 n 
 /'"$tail"'/ {
  p
  b
 } 
 ba
 }
p' server.xml

这两个代码都在stdout上打印输出。使用重定向,或者,为了替换内联文件,添加选项'-i'(在linux上)或'-i'“'(在BSD / OS X上)。