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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2699666/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 22:00:35  来源:igfitidea点击:

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

bashshellscriptingsedawk

提问by Ricardo Marimon

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

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

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

I want to run a script and have realm.xmlreplace the contents between the <!-- BEGIN realm -->and <!-- END realm -->lines. If realm.xmlchanges 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/tomcaton startup of the service on multiple installations on which the realm is going to be different.

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

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

我不太确定如何简单地使用awkor来做到这一点sed

回答by Paused until further notice.

Give this a try:

试试这个:

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

回答by Péter T?r?k

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

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

回答by ghostdog74

you can use awk

你可以使用 awk

awk 'FNR==NR{ _[++d]=
sed -n \
  -e "1,/<\!-- BEGIN realm -->/ p" \
  -e"/<\!-- END realm -->/,$ p" \
  -e "/<\!-- BEGIN realm -->/ r realm.xml" \
  server.xml
;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 realmline, then print what is stored in _. By setting a flag (f) to 1, the rest of the lines after BEGIN realmwill not be printed until /END realm/is detected.

realm.xml 作为第一个文件传递给 awk。FNR==NR 表示获取传入的第一个文件的记录并存储到变量中_。一旦 FNR!=NR,awk 将处理下一个文件。如果 awk 找到/BEGIN realm/,则打印该BEGIN realm行,然后打印存储在_. 通过将标志 (f) 设置为 1,在检测到BEGIN realm之前不会打印后面的其余行/END realm/

回答by Ricardo Marimon

How about this little snippet I created:

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

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

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

第一个命令打印行,<!- BEGIN realm -->第二个命令打印从 at 开始的行<!-- END realm -->,第三个命令将文本附加到文件“realm.xml”中。如果我能简化删除标记线之间<!- BEGIN realm --><!-- END realm -->不删除标记线之间的线,它就会变得如此简单。它可以inplace用 sed来完成!!!

回答by yabt

You may also use the ed command (cf. http://wiki.bash-hackers.org/howto/edit-ed):

您也可以使用 ed 命令(参见http://wiki.bash-hackers.org/howto/edit-ed):

    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'\n' +
          'insert whatever you want here\n' + 
          r'\n', data, flags=re.DOTALL)
        fout.write(data)
    EOF

回答by Steve Bennett

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:

我遇到了同样的需求(因此找到了这个问题)。在使用 sed 和 awk 太久之后,我最终意识到使用现代的、可读的、可理解的、广泛使用的语言(如 Python)并没有错:

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

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.

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

(Source: the internet)

(来源:网络)

回答by marco

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.comworks only on Linux, here I fixed it:

我无法在 OS X 上轻松获得 Dennis 解决方案(它的 BSD sed 略有不同)。我找到了另一个可以在 Linux 和 OS X 上工作的解决方案(我有一个混合环境)。superuser.com上的原始版本仅适用于 Linux,在这里我修复了它:

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

Here a version of Dennis code that works also on OS X (using multiple lines):

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

##代码##

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

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