2015-07-24 8 views
6

Desidero recuperare le informazioni dal comando scutil di Mac OSX con uno script perl.Analisi dell'output della scutil con perl

L'output generato da tale strumento è in qualche modo simile a JSON, ma finora non sono riuscito a trovare nulla che sia in grado di analizzarlo.

Esempio:

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

ho già riuscito a recuperare elementi particolari tramite espressioni regolari, ma come ho bisogno di fare diverse ricerche diverse, sto cercando un approccio più intelligente, più generico.

Ora, prima di reinventare la ruota codificando ancora-un altro perl-parser, spero che qualcuno riconosca questo formato e possa dare qualche consiglio su come analizzare questo - diciamo - una mappa hash nidificata perl.

Qualsiasi commento benvenuto.

risposta

4

In Perl, è possibile utilizzare Marpa::R2, un'interfaccia Perl su Marpa, a general BNF parser.

Ecco un rapido esempio:

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

uscita:

\[ 
    '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' 
        ] 
       ] 
       ] 
      ], 
      '}' 
      ] 
     ] 
     ] 
    ], 
    '}' 
    ] 
+0

Molto bello! Cordiali saluti, In genere è meglio includere codice e output direttamente nelle risposte anziché negli elenchi in modo che gli utenti non debbano visitare un sito esterno ed evitare la possibilità di link-rot. Mi sono preso la libertà di modificare gli elenchi nella tua risposta, spero che non ti dispiaccia. – ThisSuitIsBlackNot

+0

Certo, grazie. Buon punto sull'inclusione diretta. – rns

0

La soluzione con Marpa::R2 è in realtà un bel approccio generico. Tuttavia, non sono così felice con la mappa hash generata, che è probabilmente il pedaggio che si deve pagare per il parser generico.

mi è venuta in mente il seguente codice per ottenere un più dritto mappa hash:

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)[email protected]_; 
    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__ 

Con questo ingresso da 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 
    } 
} 

produce questo hashmap:

$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' 
     };