用perl捕获数据 - 捕获一系列文本

时间:2015-06-15 00:04:31

标签: regex perl

我正在编写代码来解析网络上的所有接口,寻找某些配置等等。

数据如下所示:

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的接口以及具有正确配置的接口。

3 个答案:

答案 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以获取每一行。
  • 过滤器'不是单词'值(所以空行和!)
  • 在whitepace上拆分每一行,以获得一个键和值对。
  • 如果未定义任何值,请将其设置为1.(在示例中为port-security
  • 使用这些键值对填充哈希。
  • 然后使用每个接口ID的哈希值更新%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;
}