我正在尝试对旧盒子和新盒子的配置进行比较,以验证可用性和状态没有改变。
我有一个由大约一千个节点生成的文件,但每个节点都有这些行(值不同)。所以我的目标是当我运行比较/差异脚本时我想提取行的值..
第一行我只需要 Ltm::Node: (大括号之间的值)
Availability (keep value after :)
State (keep value after :)
Reason (keep value after :)
Monitor (keep value after : but if exists do not include (default node monitor)
Monitor Status (keep value after :)
--------------------------------------------------
Ltm::Node: Development/10.47.63.133 (10.47.63.133)
--------------------------------------------------
Status
Availability : available
State : enabled
Reason : Node address is available
Monitor : /Common/icmp (default node monitor)
Monitor Status : up
这里有一些旧的
Ltm::Node: Common/splunk-hec1-p.mwg.com (::)
Availability : unavailable
State : enabled
Reason : No records returned
Monitor : /Common/icmp (default node monitor)
Monitor Status : fqdn-up-no-address
Ltm::Node: Common/_auto_10.72.7.122 (10.72.7.122)
Availability : unknown
State : enabled
Reason : Node address does not have service checking enabled
Monitor : Common/none
Monitor Status : unchecked
Ltm::Node: Common/_auto_10.72.12.148 (10.72.12.148)
Availability : unknown
State : enabled
Reason : Node address does not have service checking enabled
Monitor : Common/none
Monitor Status : unchecked
这是一些新内容,以便您可以看到差异(微小但困难,因为比较显示名称差异)
Ltm::Node: splunk-hec1-p.mwg.com (::)
Availability : unavailable
State : enabled
Reason : No records returned
Monitor : /Common/icmp (default node monitor)
Monitor Status : fqdn-up-no-address
Ltm::Node: _auto_10.72.7.122 (10.72.7.122)
Availability : unknown
State : enabled
Reason : Node address does not have service checking enabled
Monitor : Common/none
Monitor Status : unchecked
Ltm::Node: _auto_10.72.12.148 (10.72.12.148)
Availability : unknown
State : enabled
Reason : Node address does not have service checking enabled
Monitor : Common/none
Monitor Status : unchecked
为什么?从旧盒子迁移到新盒子,旧盒子不遵循任何标准命名约定,而新盒子则遵循任何标准命名约定,因此名称 Development/10.47.63.133 (或其他任何名称)几乎可以保证有所不同。
我想从旧盒子中将所有信息导出到 .txt 文件(上面是文件的片段),并在新盒子上执行相同的操作,然后运行一个脚本来进行比较以查找任何差异。希望是显示不匹配的内容,以便我可以快速攻击并寻找解决方案。
有人建议 AWK ..我尝试了 diff 并且可以显示所有行,但我有超过一千个节点,并且需要很长时间。尝试使用 Ltm::Node: (10.47.63.133) 作为在新旧之间匹配的索引,并且下面的所有内容将被比较以查看是否有任何不匹配的内容。
很抱歉造成混乱..不知道如何描述这种头痛。
答案1
这种数据可以作为多级关联数组(或 Perl 术语中的 Hash-of-Hashes / HoH,请参阅佩尔兹卡,perl 数据结构手册),第一级键是节点名称,第二级键(我在下面的脚本中称为“子键”)是相关字段名称(可用性、状态、原因等)。
例如:
#!/usr/bin/perl
use strict;
die "Usage $0 [oldfile] [newfile]\n" unless (@ARGV == 2) ;
# remember both filename args
my ($oldfile,$newfile) = @ARGV[0,1];
die "$oldfile is not readable or does not exist\n" unless -r $oldfile;
die "$newfile is not readable or does not exist\n" unless -r $newfile;
# Hash variables to hold old and new data
my (%old, %new);
# Hash reference variable pointing to the hash we want
# the main loop to populate at any given moment.
# Starts off pointing to %old, changes to %new after the
# first file reaches end-of-file.
# See https://perldoc.perl.org/perlreftut and
# https://perldoc.perl.org/perlref
my $hashref = \%old;
# variable to hold the name of the current node name as
# the records in the input files are read in.
my $node;
# read and parse input files
while(<>) {
chomp;
s/^\s*|\s*$//g; # strip leading and trailing whitespace
s/\s+:\s+/ : /; # strip excess whitespace around first :
if (/^Ltm::Node:.*\s+\((.*)\)/) {
$node = $1;
$hashref->{$node}{name} = $node;
} elsif (/ : /) {
my ($key, $val) = split / : /,$_, 2;
$hashref->{$node}{$key} = $val
} else {
print STDERR "Unknown data '$_' on line $. of $ARGV\n";
};
if (eof) {
close(ARGV); # reset line counter
$hashref = \%new; # start populating %new instead of %old
}
};
# compare the keys from both files
my @common_keys = ();
foreach my $k (keys %old) {
if (exists($new{$k})) {
push @common_keys, $k;
} else {
print "Node $k found in $oldfile but not in $newfile\n"
};
};
foreach my $k (keys %new) {
if (! exists($old{$k})) {
print "Node $k found in $newfile but not in $oldfile\n";
};
}
# The list of sub-keys we care about.
my @subkeys = ('Availability', 'State', 'Reason', 'Monitor',
'Monitor Status');
# now compare sub-keys in each of the nodes
foreach my $k (@common_keys) {
foreach my $sk (@subkeys) {
if ($old{$k}{$sk} ne $new{$k}{$sk}) {
printf "[%-15s %-14s] Old = \"%s\", new = \"%s\"\n", $k, $sk,
$old{$k}{$sk}, $new{$k}{$sk};
}
}
}
将其另存为,例如compare.pl
,使其可执行chmod +x compare.pl
并像这样运行它:
$ ./compare.pl old.txt new.txt
Node 10.72.12.150 found in old.txt but not in new.txt
Node 10.72.12.149 found in new.txt but not in old.txt
[10.72.12.148 State ] Old = "enabled", new = "xenabled"
[10.72.7.122 Reason ] Old = "Node address does not have service checking enabled", new = "xNode address does not have service checking enabled"
注意:除了行上的细微差别之外Ltm::Node
,两个输入文件中的数据是相同的,因此我必须通过编辑 new.txt 在x
某些字段之前添加 an 来做出一些差异。我还将节点 10.172.12.150 添加到 old.txt,将 10.172.12.149 添加到 new.txt。
另外值得注意的是,perl 哈希本质上是无序的,因此输出可能会在每次运行时以不同的顺序打印节点差异。通过%old
在填充数组时进行排序来获得一致的顺序是很容易的@common_keys
,但是您必须实现自然排序/版本排序子例程(或使用其中之一)自然排序模块在CPAN) 以便 IP 地址正确排序。我将把这个增强功能留给读者作为练习——对于这个例子来说,这不是必需的。
您可以编辑打印语句以更改输出以满足您的需要。您没有指定输出,所以我只是打印了似乎需要轻松识别差异的内容。
在 awk 中编写类似的东西并不困难(尤其是 GNU awk,因为它对多维数组有合理的支持),但我更喜欢 perl,即使它往往比 awk 更冗长(实际上部分)因为的)。