代码之家  ›  专栏  ›  技术社区  ›  Ricardo Marimon

将文件中带分隔符的文本块替换为另一个文件的内容

  •  6
  • Ricardo Marimon  · 技术社区  · 14 年前

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

    假设有以下简化文件:

    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"/>
    

    我想运行一个脚本 Real.xml 替换 <!-- BEGIN realm --> <!-- END realm --> 线。如果 Real.xml 更改,然后每当再次运行脚本时,它将用新内容替换行 Real.xml . 这是为了磨合 /etc/init.d/tomcat 在领域将不同的多个安装上启动服务时。

    我不太确定我该怎么做 awk sed .

    7 回复  |  直到 7 年前
        1
  •  13
  •   Dennis Williamson    11 年前

    尝试一下:

    sed -i -ne '/<!-- BEGIN realm -->/ {p; r realm.xml' -e ':a; n; /<!-- END realm -->/ {p; b}; ba}; p' server.xml
    
        2
  •  3
  •   Péter Török    14 年前
    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…我认为这不是一个排他性的要求:—)

        3
  •  2
  •   ghostdog74    14 年前

    你可以使用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表示获取传入的第一个文件的记录并存储到变量 _ . awk将处理下一个文件一次fnr!=nr.如果awk发现 /BEGIN realm/ 打印 BEGIN realm 行,然后打印存储内容 γ . 通过将标志(f)设置为1,之后的其余行 开始王国 直到 /END realm/ 检测到。

        4
  •  1
  •   Ricardo Marimon    14 年前

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

    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”中。如果我能简化删除 <!-开始领域--> <!--最终领域--> 如果不删除标记线,它将尽可能简单。而且可以做到 inplace 用SED!!!!

        5
  •  1
  •   yabt    14 年前

    您也可以使用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
    
        6
  •  0
  •   Steve Bennett    11 年前

    我遇到了同样的需求(因此发现了这个问题)。在与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
    

    我想塞德和阿威已经度过了他们的一天。它们曾经是有用的,但现在很少有人能在没有文献帮助的情况下读或写。

    (来源:互联网)

        7
  •  0
  •   Community CDub    7 年前

    我无法轻松获得丹尼斯的解决方案在OSX上工作(它的BSD SED略有不同)。我发现了另一个解决方案,我可以在Linux和OSX上都工作(我有一个混合环境)。上的原始版本 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上)以内联替换文件。