如何在模式之前将文件的内容插入到另一个文件中

时间:2015-05-28 11:42:27

标签: linux awk sed

我有一个文件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&gt; / rBfile'Afile

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

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

6 个答案:

答案 0 :(得分:3)

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

#!/bin/sed -f

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

然后你可以这样称呼它:

sed -f sedscript Afile

答案 1 :(得分:3)

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 :(得分:2)

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

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

r操作之后,Normaly将脚本循环到下一行(并且没有读取此行的其余脚本)但是在N之后,它继续并将该行保留在缓冲区中以便进行操作(在这种情况下,下一个)。

所以只有在存储之后有一条线时才能工作(在这种情况下可以使用if / the / else动作添加测试)

答案 3 :(得分:2)

只是使用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使用</storage>标记作为参考:

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>

但是如果您真的需要寻找</disk>,我会做类似的事情:

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>

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

这可能适合你(GNU sed):

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

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