http://qs321.pair.com?node_id=1185438

Sonali has asked for the wisdom of the Perl Monks concerning the following question:

Hello Perl monks,

I am a Perl newbie

I am try to get the name of the hash which is in another Perl file, and then access the hash elements, but when I run my program I am getting these errors

Use of uninitialized value $line in pattern match (m//) at test.pm line 17.

Use of uninitialized value $typ_s in string at test.pm line 20.

My code goes like this

package hash; use strict; use warnings; $test = { 'hash1' => { 'paramA' => '00' , 'paramB' => 'FF' , }, 'hash2' => { 'paramA' => '01' , 'paramB' => '02' , }, 'hash3' => { 'paramA' => '00' , 'paramB' => '03' , }, };

This hash structure is generated, so I can't modify it

This the Perl file which contains the hash that needs to be extracted

This is the Perl script I have written to extract the hash

#!/usr/local/bin/perl use strict; use warnings; Generate(); sub Generate { Process_File('hash.pm'); } sub Process_File { my $filename = shift; open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file '$filename' $!"; my $line; $line =~ m/\'hash1\'/; my $typ_s = $line->{paramA}; my $paramB = $line->{paramB}; print "$typ_s"; }

Help me!

Replies are listed 'Best First'.
Re: Accessing the hash name in perl
by duyet (Friar) on Mar 22, 2017 at 12:34 UTC
    It can be easier :) Define your hash in eg. My_var.pm:
    package My_var; use strict; use warnings; our $test = { 'hash1' => { 'paramA' => '00' , 'paramB' => 'FF' , }, 'hash2' => { 'paramA' => '01' , 'paramB' => '02' , }, 'hash3' => { 'paramA' => '00' , 'paramB' => '03' , }, }; 1;

    Mind "our $test;" and "1;" at the end of package file. Info on our.

    In your script, just include the package

    #!/usr/local/bin/perl use strict; use warnings; # CPAN package use Data::Dumper; # private package use My_var; print "$My_var::test->{hash1}{paramA}\n"; print Dumper( $My_var::test );

    Output:

    00 $VAR1 = { 'hash1' => { 'paramB' => 'FF', 'paramA' => '00' }, 'hash2' => { 'paramA' => '01', 'paramB' => '02' }, 'hash3' => { 'paramB' => '03', 'paramA' => '00' } };
Re: Accessing the hash name in perl
by 1nickt (Canon) on Mar 22, 2017 at 12:58 UTC

    As hippo said, your code goes about your task in an over-complicated way. Just load the library normally and call a function in it. ( See the monastery's Tutorials on modules. )

    MyLib.pm :

    package MyLib; use strict; use warnings; sub get_data { my %hash = ( A => { a => 1, b => 2, }, B => { a => 3, b => 4, }, ); return \%hash; } 1;
    my_script.pl :
    use strict; use warnings; use MyLib; my $hashref = MyLib::get_data(); my %hash = %{ $hashref }; for my $key ( keys %hash ) { printf( 'Value of `a` in %s is %s', $key, $hash{ $key }->{'a'} ); } __END__

    Or, use a data format suited to hashes:

    my_data.yaml :

    --- A: a: 1 b: 2 B: a: 3 b: 4

    my_script2.pl :

    use strict; use warnings; use YAML qw/ LoadFile /; my $hashref = LoadFile('my_data.yaml'); my %hash = %{ $hashref }; for my $key ( keys %hash ) { printf( "Value of `a` in %s is %s', $key, $hash{ $key }->{'a'} ); } __END__

    Hope this helps!


    The way forward always starts with a minimal test.

      The hash structure is generated, so I can't change the structure of it, just extract it and modify it

        The hash structure is generated, so I can't change the structure of it, just extract it

        Sometimes you just need to take out the chainsaw.

        This assumes your file is just as you put it, parts always in quotes called hash1, hash2, hash3, there always are keys in quotes called paramA and paramB, and the values of those keys are always in quotes.

        #!/usr/local/bin/perl use strict; use warnings; Generate(); sub Generate { Process_File('hash.pm'); } sub Process_File { my $filename = shift; open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file '$filename' $!"; my $file=''; while (my $line=<$fh>) { $file.=$line; } for my $n (1..3) { if ($file =~ m/\'hash$n\'.+?\{(.*?)\}/ms) { my $part=$1; my ($typ_s) = $part=~/\'paramA\'.*?\=\>.*?\'(.*?)\'/ms; my ($paramb)= $part=~/\'paramB\'.*?\=\>.*?\'(.*?)\'/ms; print "n $n typ_s $typ_s paramb $paramb\n"; } # part } # n }
        Result
        D:\goodies\pdhuck\down1\perl\monks>perl 1185438.pl n 1 typ_s 00 paramb FF n 2 typ_s 01 paramb 02 n 3 typ_s 00 paramb 03
        Im sure others can improve on this too.

Re: Accessing the hash name in perl
by hippo (Bishop) on Mar 22, 2017 at 12:28 UTC

    I would not even think of doing it that way. Consider perhaps using Exporter instead which would be much cleaner. You could also simply have a getter in the package which you call. TIMTOWTDI but yours is not the one I would choose.

Re: Accessing the hash name in perl
by haukex (Archbishop) on Mar 22, 2017 at 14:24 UTC

    I think that there are much better solutions than trying to parse a Perl file manually. However, what the best solution is depends on several things.

    • Can you change the program that generates this input file? If so, you should at least add the our and 1; as duyet showed, because the file you posted is actually not really valid Perl - if you try to run it, you'll get the error message "Global symbol "$test" requires explicit package name (did you forget to declare "my $test"?) at ..." On the other hand, if you can change how the data is stored, then it's probably much better to use a data serialization format such as JSON (using JSON::MaybeXS), for example.

    • Can you completely trust the source of this file? If so, you can make Perl parse it using one of the functions do, require, or use, as several monks have shown. However, note that this will execute arbitrary Perl code, so these functions can introduce huge security holes if any of the input files can be changed by untrusted users, for example.

    • If you're already married to Perl as a serialization format (which, as I said above, I wouldn't necessarily recommend), then I can suggest one of my own modules as a possibility to more safely parse the file - note however that it still has quite a few limitations, for example it will not parse the statements package hash; use strict; use warnings; in your input, so you'd have to strip those manually using e.g. a regex. You can read about my module at Undumping Perl.

      One way to execute possibly unsafe code is with Safe. In this case, if all the external code does is define a data structure, you might even get away with something like my $s = Safe->new; $s->rdo($filename). But I definitely agree with trying to change the input format first, or at least making it a Perl module.

        One way to execute possibly unsafe code is with Safe.

        True, but I normally don't mention Safe because I think it's a little too easy to misuse. It requires a good amount of knowledge of the Perl internals, one must know not only which opcodes need to be allowed, but exactly what each one of them does; allowing only one too many can theoretically open a door for attackers. Plus, opcodes do sometimes change (rarely, but still), so that might have to be taken into account. Finally, the module currently appears unmaintained, and IIRC, has had some security-related bugs in the past. If used properly, Safe can make eval safer, but not "safe". That's why if there's any doubt, I'd recommend to not eval at all.

Re: Accessing the hash name in perl
by Discipulus (Canon) on Mar 23, 2017 at 08:11 UTC
    Hello Sonali

    NB this was a reply of yesterday when you had no other useful answers you now have. I put this even if later to add some general consideration. The first one is: generally speaking when you need to know a variable name (what it is after the $ sigil) you are in the wrong direction.

    There are two distinct concepts (in regard to retrieve variable from external source): parsing and importing. While the first read some file and based on the content of such file populate some variable the latter (that behind the scene do the same thing) put in the current namespace variable from an external source. Manually do the parsing is generally error prone.

    There are different options to save perl datastructure into a file for a later retrieve: you can use Data::Dump output, or Storable Yaml but see the detailed replies about haw muche they are safe.

    The very precise monk afoken (read his reply carefully) wrote a useful comparison about different methods of storing/retrieving Perl datastructure: read the thread Data::Dumper JSON Storable YAML differences

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.