我一直在尝试创建配置文件解析器来解析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的存在,应用解释的过滤器。效果很好,到目前为止,以这段代码为荣。
当我想找到一个孩子的时候,问题就来了。在下面的示例中,“给定参数2”的子代将代表我的下一个挑战。
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)随着线的深入挖掘以及如何在数据结构中正确识别它们,我如何继续为孩子标记标签?
感谢您阅读以下内容:)
答案 0 :(得分:0)
我写了一些东西来做到这一点。我不知道如何将其放在metacpan上。但是,如果我知道去哪里看看,我肯定那里已经好了。这是我在perl中写的第一本文章,所以有点混乱。但是基本上,您可以键入“ gettree -l Node interface”,并且在XR设备上它将拉出所有配置。 “ gettree -s节点Description_keyword”将提取所有单个接口配置。您还可以将其与STDIN一起使用,例如“ cat文件| gettree -l接口”。
程序
#!/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选项。匹配之后的所有行都会打印,直到空白<匹配的空白为止。因此,总而言之,它将在递增之前占用每个唯一的空格。它也适用于瞻博网络和阿尔卡特。我相信它也会对其他人起作用。
请记住修改CONFIGS_DIR和PATH_TO_Gettree.pm_MODULE以匹配您的文件系统路径
答案 1 :(得分:0)