如何在多个文件的第一个匹配模式之后使用awk插入多行

时间:2018-06-26 17:23:57

标签: xml linux bash awk sed

我有一个包含许多子目录的目录,每个子目录包含一个我要编辑的config.xml文件。喜欢:

../jobs/foo_bar-v1.2_west/config.xml
../jobs/foo_bar-v1.3_west/config.xml
../jobs/foo_stuff-v1.3_east/config.xml
../jobs/foo_foo-v9.8_north/config.xml
../jobs/NOT_FOO-v0.1_whatev/config.xml
etc.

在匹配特定行的第一个实例../jobs/foo*/config.xml之后,我需要一种将多行文本插入到多个<properties>文件中的方法。

要插入的文本如下:

    <a.bunch.of.TextGoesHere>
      <permission>one.foo.Items.Foo:person.name</permission>
      <permission>two.foo.Items.Foo:person.name</permission>
      <permission>three.foo.Items.Foo:person.name</permission>
    </a.bunch.of.TextGoesHere>

每个../jobs/foo*/config.xml如下:

<?xml version='1.0' encoding='UTF-8'?>
<foo1>
  <actions/>
  <description>foo2</description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <foo3/>
  </properties>
 ...
  <lots_of_other_stuff>
  <properties>
    <junk>
  </properties>

每个config.xml的最终输出应如下所示:

<?xml version='1.0' encoding='UTF-8'?>
<foo1>
  <actions/>
  <description>foo2</description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <a.bunch.of.TextGoesHere>
      <permission>one.foo.Items.Foo:person.name</permission>
      <permission>two.foo.Items.Foo:person.name</permission>
      <permission>three.foo.Items.Foo:person.name</permission>
    </a.bunch.of.TextGoesHere>
    <foo3/>
  </properties>
 ...
  <lots_of_other_stuff>
  <properties>
    <junk>
  </properties>

我尝试使用sed在特定行之后插入,例如

#!/bin/bash
find ../jobs/run* -name config.xml -exec sed -i '6a\
<text to insert>' {} \;

但偶尔,<description>中的长config.xml文本会导致插入时出现不可预测的行号。

接下来,我尝试使用sed搜索<properties>的第一个实例,并在其后插入,例如

sed -i '0,/properties/a test' config.xml

但这导致在每行之后添加test测试,直到找到<properties>。使用sed -i '1,/有相似的结果。真丑。

我不确定是否在此Amazon Linux机器上正确使用了sed,并认为awk在这里可能会更好。有人可以协助吗?谢谢。

3 个答案:

答案 0 :(得分:1)

使用GNU awk进行就地编辑,您所需要做的就是:

awk -i inplace '
NR==FNR { text = (NR>1 ? text ORS : "") $0 }
FNR==1 { cnt=0 }
{ print }
/<properties>/ && !cnt++ { print text }
' file_containing_text_to_insert ../jobs/foo*/config.xml 

答案 1 :(得分:1)

假设要插入的文本位于名为insert的文件中:

sed -e '0,/<properties>/{/<properties>/r insert' -e '}' config.xml

r命令读取一个文件并将其附加在当前行之后;

0,/pattern/{/pattern/r filename}

确保仅pattern的第一个实例会附加文本。由于该命令必须在r读取的文件名之后结束,因此必须使用-e将其分为两部分。

要就地编辑文件,请使用sed -i(用于GNU sed)。

要对多个文件执行此操作,可以使用find

find jobs -name 'config.xml' \
    -exec sed -i -e '0,/<properties>/{/<properties>/r insert' -e '}' {} +

这要求insert文件位于运行此命令的目录中。


您的命令似乎几乎是正确的,除了您没有在范围内嵌套第二个地址以确保添加仅发生一次。

答案 2 :(得分:1)

关注我的评论并给出答案:

输入xml文件“ file.xml”

<?xml version='1.0' encoding='UTF-8'?>
<foo1>
  <actions/>
  <description>foo2</description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <foo3/>
  </properties>
 ...
  <lots_of_other_stuff />
  <properties>
    <junk />
  </properties>
</foo1>

xslt样式表“ file.xslt”

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <!-- Identity transform -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <!-- insert the new stuff before the first child of the first properties element -->
    <xsl:template match="/foo1/properties[1]/*[1]">
        <a.bunch.of.TextGoesHere>
            <permission>one.foo.Items.Foo:person.name</permission>
            <permission>two.foo.Items.Foo:person.name</permission>
            <permission>three.foo.Items.Foo:person.name</permission>
        </a.bunch.of.TextGoesHere>
        <xsl:copy-of select="."/>
   </xsl:template>
</xsl:stylesheet>

使用

的结果
$ xmlstarlet transform file.xslt file.xml 
<?xml version="1.0"?>
<foo1>
  <actions/>
  <description>foo2</description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <a.bunch.of.TextGoesHere><permission>one.foo.Items.Foo:person.name</permission><permission>two.foo.Items.Foo:person.name</permission><permission>three.foo.Items.Foo:person.name</permission></a.bunch.of.TextGoesHere><foo3/>
  </properties>
 ...
  <lots_of_other_stuff/>
  <properties>
    <junk/>
  </properties>
</foo1>

要应用于所有文件,请执行以下操作:

find . -name config.xml -exec sh -c '
    for xmlfile; do
        xmlstarlet transform xform.xslt "$xmlfile" > "$xmlfile".new &&
        ln "$xmlfile" "$xmlfile".bak &&
        mv "$xmlfile".new "$xmlfile"
    done
' sh {} +