【问题标题】:Parsing scutil output with perl用 perl 解析 scutil 输出
【发布时间】:2015-10-15 05:34:46
【问题描述】:

我想使用 perl 脚本从 Mac OSX 的 scutil 命令中检索信息。

该工具生成的输出有点类似于 JSON,但到目前为止我还没有找到任何能够解析它的东西。

例子:

scutil
> open
> show State:/Network/Service/0F70B221-EEF7-4ACC-96D8-ECBA3A15F132/IPv4
<dictionary> {
  ARPResolvedHardwareAddress : 00:1b:c0:4a:82:f9
  ARPResolvedIPAddress : 10.10.0.254
  AdditionalRoutes : <array> {
    0 : <dictionary> {
      DestinationAddress : 10.10.0.146
      SubnetMask : 255.255.255.255
    }
    1 : <dictionary> {
      DestinationAddress : 169.254.0.0
      SubnetMask : 255.255.0.0
    }
  }
  Addresses : <array> {
    0 : 10.10.0.146
  }
  ConfirmedInterfaceName : en0
  InterfaceName : en0
  NetworkSignature : IPv4.Router=10.10.0.254;IPv4.RouterHardwareAddress=00:1b:c0:4a:82:f9
  Router : 10.10.0.254
  SubnetMasks : <array> {
    0 : 255.255.255.0
  }
}

我已经设法通过正则表达式检索特定元素,但由于我需要进行几种不同的查找,我正在寻找一种更智能、更通用的方法。

现在,在我通过编写又一个 perl 解析器来重新发明轮子之前,我希望有人能识别这种格式,并能给出一些建议如何在嵌套的 perl 哈希映射中解析它。

欢迎任何 cmets。

【问题讨论】:

    标签: regex json macos perl parsing


    【解决方案1】:

    在 Perl 中,您可以使用 Marpa::R2,它是 Marpa, a general BNF parser 的 Perl 接口。

    这是一个简单的例子:

    use 5.010;
    use strict;
    use warnings;
    
    use Data::Dumper;
    $Data::Dumper::Indent = 1;
    $Data::Dumper::Terse = 1;
    $Data::Dumper::Deepcopy = 1;
    
    use Marpa::R2;
    
    my $g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),
        :default ::= action => [ name, value]
        lexeme default = action => [ name, value] latm => 1
    
        scutil ::= 'scutil' '> open' '> show' path '<dictionary>' '{' pairs '}'
        path ~ [\w/:\-]+
    
        pairs ::= pair+
        pair ::= name ':' value
        name ~ [\w]+
        value ::= ip | mac | interface | signature | array | dict
    
        ip ~ octet '.' octet '.' octet '.' octet
        octet ~ [\d]+
        mac ~ [a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]
        interface ~ [\w]+
    
        signature ::= signature_item+ separator => [;]
        signature_item ::= signature_item_name '=' signature_item_value
        signature_item_name ~ [\w\.]+
        signature_item_value ::= ip | mac
    
        dict  ::= '<dictionary>' '{' pairs '}'
        array ::= '<array>' '{' items  '}'
        items ::= item+
        item ::= index ':' value
        index ~ [\d]+
    
        :discard ~ whitespace
        whitespace ~ [\s]+
    END_OF_SOURCE
    } );
    
    my $input = <<EOI;
    scutil
    > open
    > show State:/Network/Service/0F70B221-EEF7-4ACC-96D8-ECBA3A15F132/IPv4
    <dictionary> {
      ARPResolvedHardwareAddress : 00:1b:c0:4a:82:f9
      ARPResolvedIPAddress : 10.10.0.254
      AdditionalRoutes : <array> {
        0 : <dictionary> {
          DestinationAddress : 10.10.0.146
          SubnetMask : 255.255.255.255
        }
        1 : <dictionary> {
          DestinationAddress : 169.254.0.0
          SubnetMask : 255.255.0.0
        }
      }
      Addresses : <array> {
        0 : 10.10.0.146
      }
      ConfirmedInterfaceName : en0
      InterfaceName : en0
      NetworkSignature : IPv4.Router=10.10.0.254;IPv4.RouterHardwareAddress=00:1b:c0:4a:82:f9
      Router : 10.10.0.254
      SubnetMasks : <array> {
        0 : 255.255.255.0
      }
    }
    EOI
    
    say Dumper $g->parse( \$input, { trace_terminals => 0 } );
    

    输出:

    \[
        'scutil',
        'scutil',
        '> open',
        '> show',
        [
          'path',
          'State:/Network/Service/0F70B221-EEF7-4ACC-96D8-ECBA3A15F132/IPv4'
        ],
        '<dictionary>',
        '{',
        [
          'pairs',
          [
            'pair',
            [
              'name',
              'ARPResolvedHardwareAddress'
            ],
            ':',
            [
              'value',
              [
                'mac',
                '00:1b:c0:4a:82:f9'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'ARPResolvedIPAddress'
            ],
            ':',
            [
              'value',
              [
                'ip',
                '10.10.0.254'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'AdditionalRoutes'
            ],
            ':',
            [
              'value',
              [
                'array',
                '<array>',
                '{',
                [
                  'items',
                  [
                    'item',
                    [
                      'index',
                      '0'
                    ],
                    ':',
                    [
                      'value',
                      [
                        'dict',
                        '<dictionary>',
                        '{',
                        [
                          'pairs',
                          [
                            'pair',
                            [
                              'name',
                              'DestinationAddress'
                            ],
                            ':',
                            [
                              'value',
                              [
                                'ip',
                                '10.10.0.146'
                              ]
                            ]
                          ],
                          [
                            'pair',
                            [
                              'name',
                              'SubnetMask'
                            ],
                            ':',
                            [
                              'value',
                              [
                                'ip',
                                '255.255.255.255'
                              ]
                            ]
                          ]
                        ],
                        '}'
                      ]
                    ]
                  ],
                  [
                    'item',
                    [
                      'index',
                      '1'
                    ],
                    ':',
                    [
                      'value',
                      [
                        'dict',
                        '<dictionary>',
                        '{',
                        [
                          'pairs',
                          [
                            'pair',
                            [
                              'name',
                              'DestinationAddress'
                            ],
                            ':',
                            [
                              'value',
                              [
                                'ip',
                                '169.254.0.0'
                              ]
                            ]
                          ],
                          [
                            'pair',
                            [
                              'name',
                              'SubnetMask'
                            ],
                            ':',
                            [
                              'value',
                              [
                                'ip',
                                '255.255.0.0'
                              ]
                            ]
                          ]
                        ],
                        '}'
                      ]
                    ]
                  ]
                ],
                '}'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'Addresses'
            ],
            ':',
            [
              'value',
              [
                'array',
                '<array>',
                '{',
                [
                  'items',
                  [
                    'item',
                    [
                      'index',
                      '0'
                    ],
                    ':',
                    [
                      'value',
                      [
                        'ip',
                        '10.10.0.146'
                      ]
                    ]
                  ]
                ],
                '}'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'ConfirmedInterfaceName'
            ],
            ':',
            [
              'value',
              [
                'interface',
                'en0'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'InterfaceName'
            ],
            ':',
            [
              'value',
              [
                'interface',
                'en0'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'NetworkSignature'
            ],
            ':',
            [
              'value',
              [
                'signature',
                [
                  'signature_item',
                  [
                    'signature_item_name',
                    'IPv4.Router'
                  ],
                  '=',
                  [
                    'signature_item_value',
                    [
                      'ip',
                      '10.10.0.254'
                    ]
                  ]
                ],
                [
                  'signature_item',
                  [
                    'signature_item_name',
                    'IPv4.RouterHardwareAddress'
                  ],
                  '=',
                  [
                    'signature_item_value',
                    [
                      'mac',
                      '00:1b:c0:4a:82:f9'
                    ]
                  ]
                ]
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'Router'
            ],
            ':',
            [
              'value',
              [
                'ip',
                '10.10.0.254'
              ]
            ]
          ],
          [
            'pair',
            [
              'name',
              'SubnetMasks'
            ],
            ':',
            [
              'value',
              [
                'array',
                '<array>',
                '{',
                [
                  'items',
                  [
                    'item',
                    [
                      'index',
                      '0'
                    ],
                    ':',
                    [
                      'value',
                      [
                        'ip',
                        '255.255.255.0'
                      ]
                    ]
                  ]
                ],
                '}'
              ]
            ]
          ]
        ],
        '}'
      ]
    

    【讨论】:

    • 非常酷!仅供参考,通常最好将代码和输出直接包含在您的答案中而不是要点中,这样用户就不必访问外部站点并避免链接腐烂的可能性。我冒昧地将要点编辑到您的答案中,希望您不介意。
    【解决方案2】:

    Marpa::R2 的解决方案实际上是一个很好的通用方法。 但是,我对生成的哈希映射不太满意,这可能是为通用解析器付出的代价。

    我想出了以下代码来获得更的哈希图:

    use Data::Dumper;
    
    open(my $pipe, '-|', "scutil <<- end_scutil 2> /dev/null
    open
    show State:/Network/Service/21AD96AA-AD28-4D5C-93C1-F343FD07DA60/IPv4
    close
    end_scutil") or die $!;
    
    sub doParse {
      my ($type)=@_;
      my $map;
      my @arr;
    
      while(<$pipe>) {
        chomp;
        if ($type eq "dictionary") {
          if (m/^<dictionary> \{/) {
            $map=doParse("dictionary");
          } elsif (m/\s*([^:]+) : <(.*)> \{/) {
            $map->{$1}=doParse($2);
          } elsif (m/\s*([^:]+) : ([^\}]+)$/) {
            $map->{$1}=$2;
          } elsif (m/\}$/) {
            return $map;
          } else {
            print STDERR "$type parse error on $_";
          }
        } elsif ($type eq "array") {
          if (m/\s*(\d+) : <(.*)> \{/) {
            $arr[$1]=doParse($2);
          } elsif (m/\s*(\d+) : ([^\}]+)$/) {
            $arr[$1]=$2;
          } elsif (m/\}$/) {
            return \@arr;
          } else {
            print STDERR "$type parse error on $_";
          }
        }
      }
      return $map;
    }
    
    print Dumper(doParse("dictionary"));
    
    1;
    __END__
    

    来自scutil的这个输入

    <dictionary> {
      ARPResolvedHardwareAddress : 00:1e:8c:72:27:d2
      ARPResolvedIPAddress : 192.168.1.10
      AdditionalRoutes : <array> {
        0 : <dictionary> {
          DestinationAddress : 192.168.1.232
          SubnetMask : 255.255.255.255
        }
        1 : <dictionary> {
          DestinationAddress : 169.254.0.0
          SubnetMask : 255.255.0.0
        }
      }
      Addresses : <array> {
        0 : 192.168.1.232
      }
      ConfirmedInterfaceName : en0
      InterfaceName : en0
      NetworkSignature : IPv4.Router=192.168.1.10;IPv4.RouterHardwareAddress=00:1e:8c:72:27:d2
      Router : 192.168.1.10
      SubnetMasks : <array> {
        0 : 255.255.255.0
      }
    }
    

    它产生这个哈希图:

    $VAR1 = {
              'InterfaceName' => 'en0',
              'Addresses' => [
                               '192.168.1.232'
                             ],
              'ARPResolvedHardwareAddress' => '00:1e:8c:72:27:d2',
              'NetworkSignature' => 'IPv4.Router=192.168.1.10;IPv4.RouterHardwareAddress=00:1e:8c:72:27:d2',
              'ARPResolvedIPAddress' => '192.168.1.10',
              'AdditionalRoutes' => [
                                      {
                                        'SubnetMask' => '255.255.255.255',
                                        'DestinationAddress' => '192.168.1.232'
                                      },
                                      {
                                        'DestinationAddress' => '169.254.0.0',
                                        'SubnetMask' => '255.255.0.0'
                                      }
                                    ],
              'Router' => '192.168.1.10',
              'SubnetMasks' => [
                                 '255.255.255.0'
                               ],
              'ConfirmedInterfaceName' => 'en0'
            };
    

    【讨论】:

      猜你喜欢
      • 2016-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多