如何将一个文件的内容插入到另一个文件的两个匹配行之间?

如何将一个文件的内容插入到另一个文件的两个匹配行之间?

我有一个 xml 文件 process.xml,我想将 tmp.xml 的内容插入其中。但需要注意的是,我需要将这些内容插入两个匹配模式之间。这是 process.xml 文件的片段

$cat process.xml
...
<fork name="data">
      <path start="process_x" />
      <path start="process_y" />
      <path start="process_z" />
</fork>
...
...
<action name="process_x" />
....
....
</action>
<action name="process_z" />
....
....
</action>

这是 tmp.xml 文件的内容

$ cat tmp.xml
<path start="process_a" />
<path start="process_b" />

我的匹配模式将是“ process_z”和“ </fork>”,内容应该粘贴在这些模式之间。这是我尝试过的..

string=$(tac process.xml | grep -m1 -oP '(?<=path start="process_).*(?=" />)')
search="process_$string"
sed -e "/$search/ r tmp.xml" "process.xml"

但它会将内容插入到andtmp.xml中。不过我只需要它在这样的内部。forkactionfork

...
<fork name="data">
      <path start="process_x" />
      <path start="process_y" />
      <path start="process_z" />
      <path start="process_a" />
      <path start="process_b" />
</fork>
...
...

任何帮助深表感谢。

答案1

看起来您想在最后一次出现<path start="process_.

你可以这样做:

awk '
  /path start="process_/ {print saved $0; saved=""; n++; next}
  n {saved = saved $0 RS; next}
  {print}
  END{system("cat tmp.xml"); printf "%s", saved}' process.xml

但这意味着将文件从最后一次出现path start="process_到末尾的部分存储在内存中。

或者你可以使用以下命令来获取内存中的整个文件:

perl -0777 -pe 's/.*path start="process_.*?\n\K/<STDIN>/se
               ' process.xml < tmp.xml

检查下</fork>一个非空行上的 的变体:

perl -0777 -pe 's{.*path start="process_[^\n]*\n\K(?=\s*</fork>)}{<STDIN>}se
               ' process.xml < tmp.xml

对齐缩进并添加额外换行符(如果缺少)的变体tmp.xml

perl -0777 -pe 's{(?s:.*)(^\h*).*path start="process_.*\n\K(?=\s*</fork>)}{
 $insert = <STDIN>;
 $indent = $1;
 $insert =~ s/^/$indent/gm;
 $insert =~ s/\n?$/\n/;
 $insert}me' process.xml < tmp.xml

使用 -0777 -pe 'code' fileperl运行code$_其中 是 的内容,然后file打印该内容$_(此处由 修改code)。

在那里,我们只有一个替换命令s{pattern}{replacement}flags

获取所有这些命令中最后一次出现的模式的技巧是前导贪婪.*(这里位于s标志下,因此它也匹配换行符)。由于它是贪婪的,它会尝试匹配尽可能多的字符,直到^(带有标志的行的开头m)后跟一系列水平空白(\h*),我们将其捕获$1(\h*)然后是我们的模式,然后是该行的其余部分(.*此没有标志的时间s,因此不会吞噬换行符)后跟换行符。

之后我们添加一个\K来告诉perl这是匹配文本的开始。然后我们有一个前瞻运算符来验证换行符后面是否跟着一系列空格 ( \s*) 和</fork>

在替换中,我们从 stdin 中获取内容tmp.xml,并在每行的开头插入捕获的缩进,如果缺少则添加尾随换行符,并将其作为替换。


另一种方法是处理该文件两次。一次检索模式最后一次出现的行号,第二次将文件插入其中:

sed "$(awk '/path start="process_/{n=NR};END{print n}' < process.xml  
      )r tmp.xml" process.xml

或者,也许您可​​以将其插入之前</fork>

awk '/<\/fork>/{system("cat tmp.xml")};1' < process.xml

答案2

<fork>假设您的文件中只有一个...

添加</fork>到插入字符串的末尾。

替换</fork>为您修改后的插入字符串。

更新:由于操作标记也包含相同的进程名称,因此展开“搜索”字符串以便找到整个标记<path start="process_$string" />。或者足够消除“动作”匹配,如下所示: t="process_$string"

相关内容