Dynamic variable names and hash keys

Viewed 112

My script written in Perl parse text output of a command which is structured like this:

Controller information
   Controller Status                     : Optimal
Logical device information
Logical Device number 1
   Logical Status                        : Optimal
Logical Device number 2
   Logical Status                        : Optimal

Parser should fill multidimentional structure:

{
  "controller": {
    "Controller Status": "Optimal"
  }
  "logical": [
    {
      "Logical Status": "Optimal"
    },
    {
      "Logical Status": "Optimal"
    }
  ]
}

I try parsing using dynamic variables:

foreach (@lines) {
  $variable = "info{controller}" if (m/Controller information/);
  $variable = "info{logical}[$1]" if (m/Logical Device number (\d+)/);

  ${$variable}{1} = $2 if (m/\s+(.*?)\s+:\s(.*)$/);
}

In this case header will set hash key corresponding to the topic and all following parameters will be put into chosen key.

The first problem is that dynamic ${$variable} will not work if $variable contain any hash or array keys. Is there a way to make dynamic variables work with hash keys inside?

The second problem is that dynamic variables are called "always-a-bad-idea", and I wonder if there is short but effective way to build parsing function without dynamic variables?

1 Answers

Use Data::DeepAccess (or Data::Diver with a bit different interface).

Note that I used $1 - 1 as the index, otherwise the first element with index 0 under logical was undef.

#!/usr/bin/perl
use warnings;
use strict;

use Data::DeepAccess qw{ deep_set };

my %info;

my @where;
while (<DATA>) {
    @where = ('controller')                 if /Controller information/;
    @where = ('logical', {index => $1 - 1}) if /Logical Device number (\d+)/;

    deep_set(\%info, @where, "$1", "$2") if m/\s+(.*?)\s+:\s(.*)$/;
}

use Data::Dumper; print Dumper \%info;

__DATA__
Controller information
   Controller Status                     : Optimal
Logical device information
Logical Device number 1
   Logical Status                        : Optimal
Logical Device number 2
   Logical Status                        : Optimal

Or, use references:

#!/usr/bin/perl
use warnings;
use strict;

my %info;

my $where;
while (<DATA>) {
    $where = \$info{controller}        if /Controller information/;
    $where = \$info{logical}[ $1 - 1 ] if /Logical Device number (\d+)/;

    $$where->{$1} = $2 if m/\s+(.*?)\s+:\s(.*)$/;
}
...
Related