我正在编写代码来解析网络上的所有接口,寻找某些配置等等。
数据如下所示:
Interface fa1
mode access
port-security
mac-address sticky
!
interface fa2
mode trunk
!
基本上以" ^ interface"开头并结束"!"。
我目前的算法是"记录"我需要的数据
foreach $line (@input) {
if ( $line =~ m/^interface.+\d/ && $line !~ m/interface Embedded-Service-Engine|BRI|TenGigabitEthernet|vlan|Port-channel|ATM|loopback/i) {
$record = 1;
}
#$int ne '' is to handle the rest of the file not in this format
if( $line =~ m/!/ && $int ne '') {
#save data in format 'interface fa2,mode trunk'
#if the interface doesn't have port-security
push(@intlist, join(','split("\r\n",$int))."\n") unless $int =~ m/port-security/;
$record=0;
$int='';
}
if ($record) {
$int.=$line;
}
}
虽然这适用于我的情况,但我想要一个简单的方法来做到这一点。我已经搜索过,发现你可以使用范围运算符' ..'在正则表达式
将我的代码转换为:
@input # contains the file
@interfaces = grep (/^interface.+\d/ .. /!/, @input);
它给了我所有的接口数据,问题是现在每一行都是@interfaces
数组中的单个元素。那我怎么能把这个数据拆分起来,所以/^interface.+\d/ .. /!/
中的所有内容都是这个数组中的一个元素而没有创建更多的for循环?
目标是将其归结为一个元素,以便我可以扫描它以查找我不想查看interface Embedded-Service-Engine|BRI|TenGigabit
的接口以及具有正确配置的接口。
答案 0 :(得分:1)
看看$/
,因为我认为这会有所帮助。它是记录分隔符 - 默认为\n
。
然后,您可以将正则表达式应用于当前的“块”以提取所需的数据 - 默认情况下,正则表达式/捕获组应用于隐式变量$_
。
E.g。
#!/usr/bin/perl
use strict;
use warnings;
local $/ = '!';
while ( <DATA> ) {
my ( $interface ) = m/Interface (\w+)/i;
next if $interface =~ m/Embedded-Service-Engine/;
my ( $mode ) = m/mode (\w+)/;
print "$interface $mode\n";
print "---next record---\n";
}
__DATA__
Interface fa1
mode access
port-security
mac-address sticky
!
interface fa2
mode trunk
!
如果您需要将数据保留用于其他用途(例如,“当前进程”不合适),则该作业的工具是哈希。
您可以使用类似上面的内容 - 并使用您感兴趣的特定键填充哈希值 - 或者使用地图的魔力为您执行此操作。
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
local $/ = '!';
my %interfaces;
while ( <DATA> ) {
my ( $interface ) = m/Interface (\w+)/i;
next if $interface =~ m/Embedded-Service-Engine/;
my %interface_values = map { my ( $key, $value ) = split; $key, $value || 1 } grep { /\w/ } split ( "\n" );
$interfaces{$interface} = \%interface_values;
}
print Dumper \%interfaces
__DATA__
Interface fa1
mode access
port-security
mac-address sticky
!
interface fa2
mode trunk
!
基本上map
行:
\n
以获取每一行。port-security
)%interfaces
。 给出类似的东西:
$VAR1 = {
'fa1' => {
'port-security' => 1,
'mode' => 'access',
'Interface' => 'fa1',
'mac-address' => 'sticky'
},
'fa2' => {
'mode' => 'trunk',
'interface' => 'fa2'
}
};
答案 1 :(得分:1)
哈希或hashref将是您可以使用的结果。此外,可以使用匹配的正则表达式来读取基于固定结构的读取记录。像这样:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
our %MATCH;
*MATCH = \%+;
# read file into variable
my ( $file, $data ) = ( 'interfaces.txt', undef );
open( my $fh, '<', $file ) or die "cannot open file $file";
{
local $/;
$data = <$fh>;
}
close($fh);
print Dumper $data;
my $regex = qr{
(?sm)
interface [^\w\n]+
(?<interface> (\w[^\n]+))
[^\w]+
mode [^\w]+
(?<mode> (\w[^\n]+))
[^\w]+
((?<portsecurity> port-security)
[^\w]+)? # port-security is optional
(mac-address [^\w]+
(?<macaddress> (\w[^\n]+))
)? # mac-address is optional
[^!]*
!
}x;
my $results = {};
while ( $data =~ m/$regex/g ) {
my $interface = $MATCH{interface};
$results->{$interface} = { mode => $MATCH{mode} ? $MATCH{mode} : '' };
$results->{$interface}->{'port-security'} = 1
if defined $MATCH{portsecurity};
$results->{$interface}->{macaddress} = $MATCH{macaddress}
if defined $MATCH{macaddress};
}
print Dumper $results;
您输入的结果是:
$VAR1 = {
'fa1' => {
'macaddress' => 'sticky',
'mode' => 'access',
'port-security' => 1
},
'fa2' => {
'mode' => 'trunk'
}
};
使用接口名称作为键值的哈希,使您有机会为所需的接口使用“grep”。
如果您的结构没有修复 - 在您的字段模式,端口安全性,mac-address中没有排序 - 那么您需要一次性读取接口记录,并使用单独的正则表达式分割字段字段。
答案 2 :(得分:0)
这是我的最终解决方案。在这种特殊情况下,我搜索所有最大端口安全性不等于1的交换机端口。这只是一个示例,可以针对任何配置进行切换。如果实际应用了这些配置,我也会忽略某些接口被捕获。
#!/usr/bin/perl
$MDIR='/currentConfig';
#list of interfaces you don't want to see to filter output
@omit =(
'MANAGEMENT.PORT',
'sup.mgmt',
'Internal.EtherSwitch',
'Router',
'ip address \d',
'STRA'
);
#join with '|' to form the regex
$dontwant = join('|',@omit);
#search criteria
$search='switchport port-security maximum [^1]';
opendir(DIR,$MDIR) or die $!;
@dirContents=readdir DIR;close DIR;
foreach $file (@dirContents) {
open(IN,$MDIR.'/'.$file) or die $!;
#record seperator to !
$/='!';
my @inFile=<IN>; close IN;
#since the record seperator has been changed, '^' won't match beginning of line
my @ints = grep (/\ninterface/i,@inFile);
#set record seperator back to normal
$/="\n";
foreach $int (@ints) {
if ( $int =~ m/$search/i && $int !~ m/$dontwant/) {
push(@finalint,$int);
}
}
}
#just list the interfaces found, i'll use this to make it comma seperated
foreach $elem (@finalint) {
print $elem;
}