【问题标题】:How to insert the content of a file into another file before a pattern如何在模式之前将文件的内容插入到另一个文件中
【发布时间】:2015-05-29 17:45:34
【问题描述】:

我有一个文件 Afile:

<start>
<memory>
<hdd>10</hdd>
<hdc>40</hdc>
</memory>
<storage>
<disk>
<disk1>firstname</disk1>
</disk>
<disk>
<disk1>secondname</disk1>
</disk>
<map>
<code>1</code>
</map> 
<map>
<code>2</code>
</map> 
</storage>
</start>

我有第二个文件 Bfile:

<disk>
<disk1>thirdname</disk1>
</disk>

如何使用 sed 将 Bfile 的内容插入 Afile。所以最后我需要有以下文件:

<start>
<memory>
<hdd>10</hdd>
<hdc>40</hdc>
</memory>
<storage>
<disk>
<disk1>firstname</disk1>
</disk>
<disk>
<disk1>secondname</disk1>
</disk>
<disk>
<disk1>thirdname</disk1>
</disk>
<map>
<code>1</code>
</map> 
<map>
<code>2</code>
</map> 
</storage>
</start>

所以它应该插入到最后一个模式之后。当我使用以下命令时,我得到以下结果:

sed -e '/disk>/rBfile' 文件

<start>
<memory>
<hdd>10</hdd>
<hdc>40</hdc>
</memory>
<storage>
<disk>
<disk1>firstname</disk1>
</disk>
<disk>
<disk1>thirdname</disk1>
</disk>
<disk>
<disk1>secondname</disk1>
</disk>
<disk>
<disk1>thirdname</disk1>
</disk>
<map>
<code>1</code>
</map> 
<map>
<code>2</code>
</map> 
</storage>
</start>

所以它把Bfile的内容放在每次出现“disk>”之后。我只需要最后一次出现。如何更改命令?

【问题讨论】:

  • 我会使用Using sed to insert file content 使用/&lt;\/storage&gt;/ 作为模式。
  • 如何在第二次出现模式后添加?所以在我的情况下,模式是/&lt;\/disk&gt;/
  • 如果您的意思是该文件可能已经包含&lt;disk1&gt;fourthname&lt;/disk1&gt;,那么您应该更新您的问题。虽然在sed 中可能是可能的,但在awk 中会容易得多。如果您可以接受awk 解决方案,还可以添加awk 标记。祝你好运。
  • awk 也很好,所以我有 Afile 和 Bfile,需要结果 &lt;start&gt; &lt;memory&gt; &lt;hdd&gt;10&lt;/hdd&gt; &lt;hdc&gt;40&lt;/hdc&gt; &lt;/memory&gt; &lt;storage&gt; &lt;disk&gt; &lt;disk1&gt;firstname&lt;/disk1&gt; &lt;/disk&gt; &lt;disk&gt; &lt;disk1&gt;secondname&lt;/disk1&gt; &lt;/disk&gt; &lt;disk&gt; &lt;disk1&gt;thirdname&lt;/disk1&gt; &lt;/disk&gt; &lt;/storage&gt; &lt;/start&gt;
  • 你确定你真的需要插入“秒后”吗?正如@fedorqui 所说,在 之前插入标签似乎更好。

标签: linux awk sed


【解决方案1】:

XML(如一般的结构化数据)不应使用纯文本工具(如 awksed)处理,除非在非常特殊的情况下,因为没有人希望 XML 工具在换行符更改位置或插入空格时中断/在良性的地方移除。

相反,我会使用 Python,它的标准库中有一个 XML 解析器:

#!/usr/bin/python

import xml.etree.ElementTree as ET;
import sys;

# file names taken from command line arguments.
target = ET.parse(sys.argv[1]);
insert = ET.parse(sys.argv[2]);

# Interesting part here:    
target.getroot().find("./storage").append(insert.getroot())

# to write to a file, use target.write('output.xml')
ET.dump(target)

称之为

python foobar.py fileA fileB

【讨论】:

  • 就是这样!加一个
【解决方案2】:

我没有设法在一行中做到这一点,所以我制作了一个 sed 脚本。问题是如果文件名后面有字符,r 命令将不起作用,因此它需要在自己的行上。

#!/bin/sed -f

/<\/disk>/{
  :a 
  n
  s/disk/disk/
  t a
  h
  r bbb
  g
  N
}

你可以这样称呼它:

sed -f sedscript Afile

【讨论】:

  • 所以模式应该是第二个而不是,我改变了文件包含
【解决方案3】:

如果受存储限制(给出第一个样本)

sed '\#</storage># {r Bfile
   N;} ' Afile

如果最后一个磁盘在存储中(就像这个请求的编辑版本)

sed '1;\#<storage>#{1h;1!H
    \#<storage># {g
       s#^\(.*\n</disk>\).*#\1#p
       r Bfile
       G;N
       s/^\(.*\)\1\(.*\)/\2/
       }
   }' Afile

通常 sed 脚本在 r 操作后循环到下一行(并且不会读取该行的其余脚本)但在 N 之后,它会继续并将该行保留在缓冲区中以进行操作(在这种情况下与下一个)。

所以只有在 storage 之后有一行时才有效(在这种情况下,可以在之前添加一个带有 if/the/else 操作的测试)

【讨论】:

    【解决方案4】:

    只是添加一些使用 AWK 的示例。

    假设我们有:

    档案

    <start>
    <memory>
    <hdd>10</hdd>
    <hdc>40</hdc>
    </memory>
    <storage>
    <disk>
    <disk1>firstname</disk1>
    </disk>
    <disk>
    <disk1>secondname</disk1>
    </disk>
    </storage>
    </start>
    

    bfile

    <disk>
    <disk1>thirdname</disk1>
    </disk>
    

    AWK 使用&lt;/storage&gt; 标签作为参考:

    awk '/^<\/storage>/{while(getline line<"bfile"){print line};print;next}1' afile
    

    这将导致:

    <start>
    <memory>
    <hdd>10</hdd>
    <hdc>40</hdc>
    </memory>
    <storage>
    <disk>
    <disk1>firstname</disk1>
    </disk>
    <disk>
    <disk1>secondname</disk1>
    </disk>
    <disk>
    <disk1>thirdname</disk1>
    </disk>
    </storage>
    </start>
    

    但如果你真的需要寻找&lt;/disk&gt;,我会这样做:

    awk -v n=4 '{print;}/<\/disk1>$/,/^<\/disk>/{m++}(m==n){n=0;while(getline l<"bfile"){print l}}' afile
    

    此外,您还可以使用xmllint为您格式化输出:

    awk -v n=4 '{print;}/<\/disk1>$/,/^<\/disk>/{m++}(m==n){n=0;while(getline l<"bfile"){print l}}' afile | xmllint --format --recover -
    

    这将导致:

    <start>
      <memory>
        <hdd>10</hdd>
        <hdc>40</hdc>
      </memory>
      <storage>
        <disk>
          <disk1>firstname</disk1>
        </disk>
        <disk>
          <disk1>secondname</disk1>
        </disk>
        <disk>
          <disk1>thirdname</disk1>
        </disk>
      </storage>
    </start>
    

    【讨论】:

    • 如何将文件名作为变量 $Afile 和 $Bfile 传递?
    【解决方案5】:

    如果ed 是一个选项(如果输入文件不是太大),那就更容易了:

    echo '/map/-1 r Bfile
    wq' | ed Afile
    

    【讨论】:

      【解决方案6】:

      这可能对你有用(GNU sed):

      sed -e '/<disk>/,${/<disk>/,/<\/disk>/b;ecat fileb' -e ':a;n;ba}' filea
      

      这将 sed 命令限制为以 &lt;disk&gt; 开头到文件末尾的那些行。在此范围内,所有完整的&lt;disk&gt;/&lt;\/disk&gt; 标签都照常打印。下一行是要插入文件的位置,并使用 sed evalute 命令立即插入文件(而不是使用 r 命令在当前模式空间之后插入文件)。然后使用简单的循环打印文件的其余部分。

      【讨论】:

        猜你喜欢
        • 2013-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-24
        • 2012-06-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多