按块名称合并两个 ini 文件

merge two ini files by their block name

如何合并两个 ini 文件的两个块?

您好,我有两个 ini 文件,它们以块的形式存储数据,如下所示:

 -->cat f1
[default]
a1=1
b1=2
c1=3

[foo]
d=1
e1=5


 -->cat f2
[default]
a2=5
b2=6

[foo]
c2=7
d2=8
e2=9

[bar]
f2=10

我需要按如下方式合并这两个文件:

[default]
a1=1
b1=2
c1=3
a2=5
b2=6

[foo]
d=1
e1=5
c2=7
d2=8
e2=9


[bar]
f2=10

老实说,我不知道从哪里开始,需要什么逻辑或工具。

我试图获取方向的一些愚蠢的事情是:

awk 'NR==FNR{a[[=12=]]=[=12=];next} [=12=] in a{print}' f1 f2
[default]

[foo]

awk -vRS='' '{=}1' f1 f2 |awk '!a[]++'
[default] a1=1 b1=2 c1=3
[foo] d=1 e1=5
[bar] f2=10

Perl 解决方案是使用像 Config::Tiny to read each, merge the resulting data structure, and write out a new file. Note this does not preserve comments or ordering (for the latter, you could use Config::Tiny::Ordered 这样的 INI 解析器,但合并更难)。

use strict;
use warnings;
use Config::Tiny;

my $config1 = Config::Tiny->read('f1');
my $config2 = Config::Tiny->read('f2');
foreach my $category (keys %$config2) {
  my $section1 = $config1->{$category} //= {};
  my $section2 = $config2->{$category};
  @$section1{keys %$section2} = values %$section2;
}
$config1->write('new');

使用 awk 你可以这样做:

awk '/^$/{
   next
}
/^\[.*\]$/{
   hdr = [=10=]
   next
}
a[hdr] != "" {
   a[hdr] = a[hdr] ORS [=10=]
   next
}
{
   a[hdr] = [=10=]
   seq[++n] = hdr
}
END {
   for (i=1; i<=n; i++)
      print seq[i] ORS a[seq[i]] (i<n ? ORS : "")
}' f1 f2

[default]
a1=1
b1=2
c1=3
a2=5
b2=6

[foo]
d=1
e1=5
c2=7
d2=8
e2=9

[bar]
f2=10

详情:

  • /^$/ 匹配我们忽略的所有空行
  • /^\[.*\]$/ 匹配 header 我们存储在 hdr 变量中的名字
  • a[hdr] != "" { ... } 当我们已经处理了 hdr 一次时,我们在索引为 hdr
  • 的数组 a 中追加一个新行和当前行
  • 否则我们只是将当前行存储在由 hdr 索引的数组 a 中。此外,我们将 hdr 存储在另一个数组 seq 中,该数组按递增数字索引以按顺序打印数据
  • END 块中,我们遍历 seq 数组并打印每个 header 和详细信息块。如果我们有更多数据要处理,我们会附加一个换行符。
$ cat tst.awk
BEGIN { RS=""; ORS="\n\n"; FS=OFS="\n" }
{ key =  }
NR == FNR { rec[key] = [=10=]; next }
key in rec {  = rec[key]; delete rec[key] }
{ print }
END {
    for (key in rec) {
        print rec[key]
    }
}

$ awk -f tst.awk file1 file2
[default]
a1=1
b1=2
c1=3
a2=5
b2=6

[foo]
d=1
e1=5
c2=7
d2=8
e2=9

[bar]
f2=10

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

diff -au999 file1 file2 | sed '1,3d;s/.//' >file3

使用diff -u999统一file1和fil2,然后删除3header行和每行的第一个字符。

N.B。如果 file1 和 file2 相同,您将不会得到任何输出,并且如果它们具有不同的值,则会重复相同的键。