2015-06-15 5 views
1

Я пишу код для разбора всех интерфейсов на моей сети, ищет для определенных конфигураций .. и т.д.Разбор данных с perl- захватив диапазон текста

данных выглядит следующим образом:

Interface fa1 
    mode access 
    port-security 
    mac-address sticky 
! 
interface fa2 
    mode trunk 
! 

В основном, начиная с «^ интерфейса» и заканчивая «!».

мой текущий алгоритм является «запись» данных мне нужно

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/ .. /!/ было одним элементом в этом массиве, не создавая больше для циклов?

Цель состоит в том, чтобы довести его до одного элемента, поэтому я могу отсканировать его для интерфейсов. Я не хочу смотреть на interface Embedded-Service-Engine|BRI|TenGigabit, а также на интерфейсы, которые имеют правильные конфигурации.

ответ

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; 
} 
+0

Хорошо. Я вижу, к чему вы клоните. Чтобы ответить на ваш вопрос «Зачем использовать усложняющий хеш?»: Это зависит. Поскольку вы просто помещаете строки в @int, ваши данные кажутся немного неструктурированными. Если вы только цель, зная, какие интерфейсы имеют подстроки $ seach, а не $ dontwant в них, я бы сказал нет. Если вам действительно нужно что-то делать с интерфейсами и их базовыми полями, то да, будет полезно использовать хэш для некоторой структуры. Я думаю, что справедливый вопрос - спросить, что будет, что вам нужно делать с вашими данными. Еще одна вещь: используйте локальный для своего разделителя. ;) –

+0

@mlambrichs Спасибо, я пытался «мой» разделитель, но не компилировался. местные работы просто прекрасны. – genx1mx6

+0

Также: «строгое» и «предупреждение» настоятельно рекомендуется. – Sobrique

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, чтобы получить каждую строку.
  • значения «не словом» фильтров (так что пустые строки и!)
  • разделяет каждую строку на белом пространстве, чтобы получить пару ключей и значений.
  • Если значение не задано, оно устанавливается в 1. (так, например, в примере port-security)
  • Задает хэш с этими парами ключ-значение.
  • , а затем обновляется %interfaces с хешем для каждого ID интерфейса.

Давать что-то вроде:

$VAR1 = { 
      'fa1' => { 
        'port-security' => 1, 
        'mode' => 'access', 
        'Interface' => 'fa1', 
        'mac-address' => 'sticky' 
        }, 
      'fa2' => { 
        'mode' => 'trunk', 
        'interface' => 'fa2' 
        } 
     }; 
+0

спасибо, что $/variable, вероятно, то, что я ищу. У меня еще не было возможности попробовать. мой сценарий сложнее, чем то, что я написал, я пытался сделать его доступным для чтения, чтобы каждый мог понять, чего я пытаюсь достичь. В конце концов, мне нужно посмотреть все интерфейсы в моей сети и извлечь все из них, которые я выбираю (те, которые не имеют определенной конфигурации, соединительные линии и т. Д.), Просто зависит от того, какой отчет я пытаюсь запустить. Быстрый вопрос, что это значит «стоит», чтобы сделать структуру более сложной, чем массив? Я просто хочу запустить regex весь интерфейс config – genx1mx6

+0

Зависит от того, что вы делаете со структурой. Хэши очень удобны, когда у вас есть ключи и ценности, как вы. Но это действительно так. – Sobrique

+1

Я понял. Благодарю. Я просто создаю отчеты, основанные на данных этого интерфейса, поэтому я могу использовать другие скрипты, чтобы пойти туда и применить конфигурации к интерфейсам, которые не имеют некоторых конфигураций по умолчанию. – genx1mx6

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 - тогда вам нужно будет прочитать запись интерфейса за один раз и разделить поля, используя отдельные регулярные выражения для каждое поле.

+0

Дело в том, что на интерфейсах может быть любое количество конфигураций. Просто зависит от его функции. - Я посмотрю на оператор $ /. благодаря! – genx1mx6

+0

Оператор $/был моим решением. Можете ли вы посмотреть мой ответ и посмотреть, как хэш сделает его лучше? мне кажется, что это слишком усложнит его. – genx1mx6

+0

Обе представленные решения используют разделитель записи. Разница заключается в том, что вам нужно читать данные за один раз ($/= '') или отделять данные в записях при чтении ($/= '!'). Я поместил свое наблюдение в качестве комментария к вашему предлагаемому решению. Думаю, нам нужно больше ввода. –