Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing scutil output with perl

I would like to retrieve information from Mac OSX's scutil command with a perl script.

The output generated by that tool is somewhat similar to JSON, but so far I failed to find anything that is capable of parsing that.

Example:

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
  }
}

I already managed to retrieve particular elements via regexps, but as I need to make several different lookups, I'm looking for a smarter, more generic approach.

Now, before I reinvent the wheel by coding yet-another-perl-parser, I hope that somebody recognizes this format and can give some advise how to parse this in - let's say - a nested perl hash map.

Any comments welcome.

like image 615
joergd Avatar asked Jul 24 '15 13:07

joergd


1 Answers

In Perl, you can use Marpa::R2, a Perl interface to Marpa, a general BNF parser.

Here is a quick example:

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 } );

Output:

\[
    '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'
                  ]
                ]
              ]
            ],
            '}'
          ]
        ]
      ]
    ],
    '}'
  ]
like image 191
rns Avatar answered Sep 28 '22 23:09

rns