我一直在尝试创建一个配置文件解析器来解析Cisco IOS配置等。最终目标是基于配置文件中的过滤器在上下文中显示相关数据。例如,使用这样的配置文件,它将显示我们发现“access vlan”行作为“interface”上下文的子节点的所有接口,并且仅显示包含“speed”,“duplex”和“description”的行。
{
'Context' => '^interface',
'Types' => [
'Switch',
],
'Condition' => 'access vlan',
'Filter' => [
'speed',
'duplex',
'description'
]
};
到现在为止还挺好。我读了“running-config”并且我在数组中索引了行深度(假设非空行,不是以空格(\ s)开头的深度为0)。
然后,在另一个读取中,我使用该索引再次读取数据,这次使用基于深度的相对位置来创建上下文的“子”。这是功能:
sub getDeep {
my @data = (@_);
my ($bighash,$hash);
#First read
foreach my $idx (0.. $#data) {
my ($spaces, $content) = ($data[$idx] =~ m/^(\s*)(.*)/);
my $depth = length $spaces;
$bighash->{node}{$idx}{depth} = $depth;
}
# Variables for the first read
my $ldepth = 0;
my $lcontext;
my $lid;
# Second read
foreach my $id (0 .. $#data) {
$data[$id] =~ s/^\s*//;
next if ($data[$id] =~ /^!/);
my $depth = $bighash->{node}{$id}{depth};
if ($depth eq 0) {
push (@{$hash->{global}} , $data[$id]);
$lcontext = $data[$id];
$lid = $id;
}
if (($depth gt 0) && ($id - $lid eq 1)) {
push (@{$hash->{$lcontext}}, (" " x $depth. $data[$id]));
$lid = $id;
}
}
return $hash;
}
使用这个子,我可以返回一个哈希值,然后根据给定键的arrayref的存在,按照解释应用过滤器。这很好用,到目前为止这段代码非常自豪。
当我想找到孩子的孩子时会出现问题。在下面的例子中,“给定param2”的孩子将代表我的下一个挑战。
interface XYZ
given param1 -> child of "interface XYZ"
given param2 -> child of "interface XYZ"
given param2.1 -> child of "given param2"
given param2.2 -> child of "given param2"
given param3 -> child of "interface XYZ"
因此,在考虑了一段时间并且采用不同方法失败之后,我的问题分为两个部分:
1)有没有更好的方法来做到这一点,我没有看到? 2)我如何继续标记孩子的孩子,因为线条深入挖掘并在数据结构中正确识别它们?
感谢您阅读此行:)
我已经写了一些事情要做到这一点。我无法弄清楚如何把它放在metacpan上。但是,如果我知道在哪里看,我肯定已经有更好的了。这是我在perl写的第一个,所以它有点混乱。但基本上你可以输入“gettree -l Node interface”,在XR设备上它会拉出所有的配置。 “gettree -s Node Description_keyword”将提取所有单个接口配置。您也可以将它与STDIN一起使用,例如“cat file | gettree -l interface”。
程序
#!/usr/bin/perl
use lib '/PATH_TO_Gettree.pm_MODULE/';
use strict;
use warnings;
use Gettree;
use Getopt::Std;
my %opts;
getopts('eislnpm' , \%opts);
my $searchstr;
my $filename;
my $debug=0;
if($ARGV[0]=~m/help/ || $ARGV[0]=~m/^\?$/ )
{ die usage(); }
if($#ARGV<0||$#ARGV>1)
{
usage();
killapp("Please specifiy node and search term, use --help for the help menu");
}
elsif($#ARGV==0)
{
Gettree::setopts( \%opts , \$ARGV[0] );
while(<STDIN>)
{
Gettree::gettree_stream_passline( \$_ );
}
print Gettree::gettree_getreturnstring();
}
else
{
$filename= $ARGV[0];
$filename="/CONFIGS_DIR/".lc $filename if ! $opts{e};
print Gettree::gettree_file ( \%opts , \$filename , \$ARGV[1]) ; #\$filename , $searchstring
}
sub killapp
{
print $_[0]."\n";
exit;
}
sub usage
{
print "
Usage: gettree [OPTION]... [NODE] STRING
Search for PATTERN in each FILE or standard input.
usage gettree <options> <node> <string>
-s include same level
-l include lower levels
-n show line numbers (do not use with STDIN, it wont work)
-i case insensitive
-e exact file location (rather than just the nodename)
-p print parent's same level lines
-m minimal print, do not print parents
Examples:
gettree Node text
gettree -sln NODE CCT_Ref
gettree -l NODE POS8/0
\n\n";
exit;
}
模
#!/usr/bin/perl
package Gettree;
use strict;
use warnings;
my $line;
my $wsdiff = 0;
my $iopt = 0;
my $sopt = 0;
my $lopt = 0;
my $nopt = 0;
my $popt = 0;
my $mopt = 0;
my $linecounter = 0;
my $matched = -1;
my $debug = 0; ##remove later
my @arr;
my @sopt_arr;
my @popt_arr;
my $searchstr;
my $returnstring;
sub setopts # \%opthash , $searchstring
{
cleardata();
push @arr, [ 0, "",0];
my %hash=%{$_[0]};
$iopt = 1 if $hash{i};
$sopt = 1 if $hash{s};
$lopt = 1 if $hash{l};
$nopt = 1 if $hash{n};
$popt = 1 if $hash{p};
$mopt = 1 if $hash{m};
if ( defined $hash{qopts} )
{
$iopt = 1 if $hash{qopts} =~ /i/;
$lopt = 1 if $hash{qopts} =~ /l/;
$nopt = 1 if $hash{qopts} =~ /n/;
$sopt = 1 if $hash{qopts} =~ /s/;
$popt = 1 if $hash{qopts} =~ /p/;
$mopt = 1 if $hash{qopts} =~ /m/;
}
if ( ref($_[1]) ) { $searchstr=$iopt? qr/${$_[1]}/i : qr/${$_[1]}/ ; }
else { $searchstr=$iopt? qr/$_[1]/i : qr/$_[1]/ ; }
}
sub gettree_stream_passline # \$line
{
process_line(${$_[0]});
}
sub gettree_getreturnstring
{
return $returnstring;
}
sub gettree_varable # \%opthash , \$text , $searchstring
{
setopts($_[0] , $_[2]);
my $str=${$_[1]};
while($str=~m#(.*\n)#g)
{
process_line($1);
}
return $returnstring;
}
sub gettree_file # \%opthash , \$filename , $searchstring
{
setopts($_[0] , $_[2]);
my $filename;
if ( ref($_[1]) ) { $filename=${$_[1]}; }
else { $filename=$_[1] ; }
open FH, "<", $filename or die "\nFile ".$filename." cannot be found\nerror : ".$!."\n";
while(my $text=<FH>)
{
process_line($text);
}
close FH;
return $returnstring;
}
sub process_line
{
$line=shift;
if($line=~m/^([ \t]+)/) { $wsdiff=length($1) }
else { $wsdiff=0 };
if($wsdiff>$arr[$#arr][0])
{
push @arr, [ $wsdiff , $line , $linecounter ];
if ( $sopt || $popt )
{
@popt_arr=@sopt_arr if $popt;
@sopt_arr=() if defined $sopt_arr[0];
}
}
else
{
while( @arr && $arr[$#arr][0]>$wsdiff )
{
pop @arr;
@sopt_arr=@popt_arr if ( $sopt || $popt );
@popt_arr=() if $popt;
}
if($#arr<0)
{
push @arr, [ $wsdiff , $line, $linecounter ];
}
else
{
push @sopt_arr, $arr[$#arr] if $sopt || $popt ;
$arr[$#arr]=[ $wsdiff , $line , $linecounter ];
}
}
@sopt_arr=() if $#sopt_arr>200; ## to avoid filling the memory
@popt_arr=() if $#popt_arr>200; ## to avoid filling the memory
##used in l and s opts to print lines after match
if($matched>=0)
{
if($wsdiff>$matched)
{
printline(\$line) if $lopt==1 ;
}
elsif ($wsdiff<$matched)
{
$matched=-1;
}
else
{
if ($sopt )
{ printline(\$line) }
else
{ $matched=-1 }
}
}
if( $matched==-1 && $line=~m/$searchstr/ )
{
printtree();
$matched=$wsdiff if $sopt || $lopt;
}
$linecounter++;
}
sub printtree
{
if(!$mopt)
{
for (0..$#arr-(1+$popt))
{
printline( \$arr[$_][1] , \$arr[$_][2] );
}
}
if($popt)
{
for (0..$#popt_arr)
{
printline( \$popt_arr[$_][1] , \$popt_arr[$_][2] );
}
printline( \$arr[$#arr-1][1] , \$arr[$#arr-1][2] ); #print the parent
@popt_arr=() ;
}
if($sopt)
{
for (0..$#sopt_arr)
{
printline( \$sopt_arr[$_][1] , \$sopt_arr[$_][2] );
}
@sopt_arr=() ;
}
printline( \$arr[$#arr][1] , \$arr[$#arr][2] );
@arr=();
push @arr, [ $wsdiff , $line , $linecounter ];
}
sub printline
{
$nopt==1? $returnstring.= ${$_[1]}+1 ." : ".${$_[0]} : $returnstring.= ${$_[0]};
}
sub cleardata
{
$line="";
$wsdiff = 0;
$iopt = 0;
$sopt = 0;
$lopt = 0;
$nopt = 0;
$popt = 0;
$mopt = 0;
$linecounter = 0;
$matched = -1;
@arr=();
@sopt_arr=();
@popt_arr=();
$searchstr="";
$returnstring="";
}
1;
Breif解释它是如何工作的程序只是模块的链接。我是一个模块,因为我已经在许多程序中使用它并且独立存在。 Gettree.pm将逐行发送数据到process_line()。进程线将获得空白区域($ wsdiff)并将其用作标记。直接在空格中增加之前的任何行都将存储在@arr中。如果稍后发现匹配,则进行打印。所以存储了父级。 @sopt_arr用于同一行,因此它存储了相同空格的前一行。 @popt_arr用于父匹配,但这不能很好地工作(我真的不使用它,它可以删除)。当匹配搜索字符串时,打印@ arr,Sopt_arr和@popt_arr,设置$ matched,这将用于-l选项。打印匹配后的所有行,直到空白区域<匹配的空白区域。总而言之,它将在增量之前采用每个唯一的空白区域。它也适用于Juniper和Alcatel。我相信它也适用于其他人。
记得修改CONFIGS_DIR和PATH_TO_Gettree.pm_MODULE以匹配您的文件系统路径