Regex 在Perl中解析(部分)非统一文本块

Regex 在Perl中解析(部分)非统一文本块,regex,perl,parsing,sed,awk,Regex,Perl,Parsing,Sed,Awk,我有一个文件,其中有几个块,在一个文件中(在程序的这一点上,在一个变量中)看起来像这样 下面是第二个块,向您展示了这些块是如何略有变化的: port-channel86 is down (No operational members) ... reliability 255/255, txload 1/255, rxload 1/255 ... Last clearing of "show interface" counters 31w2d ... RX 1476

我有一个文件,其中有几个块,在一个文件中(在程序的这一点上,在一个变量中)看起来像这样

下面是第二个块,向您展示了这些块是如何略有变化的:

port-channel86 is down (No operational members)
  ...
  reliability 255/255, txload 1/255, rxload 1/255
  ...
  Last clearing of "show interface" counters 31w2d
  ...
  RX
    147636 unicast packets  0 multicast packets  0 broadcast packets
    84356 input packets  119954232 bytes
    0 jumbo packets  0 storm suppression packets
    0 runts  0 giants  0 CRC  0 no buffer
    0 input error  0 short frame  0 overrun   0 underrun  0 ignored
    0 watchdog  0 bad etype drop  0 bad proto drop  0 if down drop
    0 input with dribble  0 input discard
    0 Rx pause
  TX
    147636 unicast packets  0 multicast packets  0 broadcast packets
    84356 output packets  119954232 bytes
    0 jumbo packets
    0 output error  0 collision  0 deferred  0 late collision
    0 lost carrier  0 no carrier  0 babble  0 output discard
    0 Tx pause
  0 interface resets
我想从每个块中挑选出某些数据元素,这些数据元素可能存在于每个块中,也可能不存在于每个块中。例如,在我发布的第一个块中,我可能想知道有0个runt、0个输入错误和0个溢出。在第二个块中,我可能想知道有0个巨型数据包、冲突等。如果给定的查询不在该块中,可以只返回na,因为这是为了统一处理而设计的

每个区块的结构与我发布的两个区块相似;新行和空格分隔某些条目,逗号分隔其他条目

我有一些关于如何工作的想法。我不知道Perl中是否有任何类型的“lookback”函数,但我可以尝试查找字段名(runts、“input errors”等),然后获取前面的整数;这似乎是最优雅的解决方案,但我不确定这是否可行

目前,我正在用Perl做这件事。我正在处理的每个“块”实际上是这些块中的几个(由双换行符分隔)。它不必在单个正则表达式中完成;我相信它可以通过在每个块上应用几个正则表达式来实现。性能不是一个真正的因素,因为这个脚本可能每小时运行一次

我的目标是以一种自动化的方式将所有这些内容转换成一个.csv文件(或其他一些易于绘制的数据格式)

有什么想法吗

编辑:如我所提到的CSV输出示例,它将逐行(对于像这样的多个条目)写入一个文件作为最终结果。如果在块中找不到特定条目,则在相应行中将其标记为na:

interface_name,txload,rxload,last_clearing,input_queue,output_drops,runts,....
vlan2,1,1,49w5d,0-75-0-0,0,0,....
port-channel86,1,1,31w2d,na,na,0,...

属性和数字的简单散列

sub extract {
    my ($block) = @_;
    my %r;
    while ($block =~ /(?<num>\d+) \s (?<name>[A-Za-z\s]+)/gmsx) {
        my $name = $+{name};
        my $num = $+{num};
        $name =~ s/\A \s+//msx;
        $name =~ s/\s+ \z//msx;
        $r{$name} = $num;
    }
    return %r;
}

my $block = <<'';
Vlan2 is up, line protocol is up
⋮

my $block2 = <<'';
port-channel86 is down (No operational members)
⋮

use Data::Dumper qw(Dumper);
print Dumper {extract $block};
print Dumper {extract $block2};
子提取{
我的($block)=@;
我的%r;
而($block=~/(?\d+)\s(?[A-Za-z\s]+)/gmsx){
我的$name=$+{name};
我的$num=$+{num};
$name=~s/\A\s+//msx;
$name=~s/\s+\z//msx;
$r{$name}=$num;
}
返回%r;
}

我的$block=我不认为一个正则表达式就可以做到这一点,如果可以的话,我也不想支持它

使用多个正则表达式,您可以轻松地使用以下内容:

(\d+) runts
(\d+) input errors
...etc...
一个简单的属性名数组和一个循环可以很快地解决这个问题,而且不需要太多麻烦


如果您可以通过一些预处理将输入分割成更小的块,那么就不太可能出现误报。

这里有一种在awk中实现的方法,但这需要大量的调整才能达到完美。 不过,还是要使用SNMP

awk '{
    printf $1
    for (i=1;i<=NF;i++) {
        if ($i" "$(i+1)~/Input queue:/) printf ",%s",$(i+2)
        if ($i~/runts/) printf ",%s",$(i-1)
        if ($i~/multicast,/) printf ",%s",$(i-1)
    }
    print ""
}' RS="swapped out" file
awk'{
打印F$1

对于(i=1;我能发布示例输出吗?完成。我希望这能回答问题。不可能从一个块的单个样本中推断出输入的总体布局。为什么不将块大小减小到只代表真实块的大小,然后发布5个左右的块,以便我们了解不同块之间的格式可能会有哪些不同块?我发布了第二个块。它们都类似于输出样式,但具有不同的字段。这足够了吗?不。再发布3个输入块,更新输出以显示在给定输入时的外观,并解释原因。同样,如果您可以将每个块减少到表示实际块的5或6行,这将非常有用,以便我们当然,你不必阅读大量不相关的数据来帮助你,一个正则表达式就可以了,多个查询就足够了。
awk '{
    printf $1
    for (i=1;i<=NF;i++) {
        if ($i" "$(i+1)~/Input queue:/) printf ",%s",$(i+2)
        if ($i~/runts/) printf ",%s",$(i-1)
        if ($i~/multicast,/) printf ",%s",$(i-1)
    }
    print ""
}' RS="swapped out" file