Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Reading a hash structure stored in a file

by sam1990 (Initiate)
on Jan 15, 2019 at 20:08 UTC ( [id://1228615]=perlquestion: print w/replies, xml ) Need Help??

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

Hello, I have a file that has a hash stored in it. I am trying to read that hash as it is using eval but I am getting following error: Global symbol "%hash1" requires explicit package name (did you forget to declare "my %hash1"?) at tiny.pl line (print Dumper(\%hash1);) 14. Execution of tiny.pl aborted due to compilation errors. Please help me understand the issue here, thank you

#file.pl : my %hash1 = (hello => 1, hi =>2 ); #tiny.pl #!/home/utils/perl5/perlbrew/perls/5.24.2-021/bin/perl use strict; use warnings; use Path::Tiny qw( path ); use Data::Dumper; my $file = 'file.pl'; open(my $fh, '<', $file) or die "Could not open file $file"; eval($fh); close $fh; print Dumper(\%hash1);

Replies are listed 'Best First'.
Re: Reading a hash structure stored in a file
by BillKSmith (Monsignor) on Jan 15, 2019 at 21:27 UTC
    You have several errors.
  • You must read the file (not just open it). Use <$fh>.
  • You must slurp the entire file (not just read the first record). Use undef $/.
  • You must not declare %hash1 as a lexical variable within the eval. (The eval string is treated as a block)
  • Under strict, you must declare %hash1 in your program.
  • #tiny.pl #!/home/utils/perl5/perlbrew/perls/5.24.2-021/bin/perl use strict; use warnings; use Path::Tiny qw( path ); use Data::Dumper; #my $file = 'file.pl'; my $file = \<<'file_end'; # Simulate disc file for post %hash1 = (hello => 1, hi =>2 ); file_end my %hash1; open(my $fh, '<', $file) or die "Could not open file $file"; local undef $/; eval( <$fh> ); close $fh; print Dumper(\%hash1); >perl tiny.pl $VAR1 = { 'hi' => 2, 'hello' => 1 };
    Bill
Re: Reading a hash structure stored in a file
by haukex (Archbishop) on Jan 15, 2019 at 21:19 UTC

    The main problem is that my declares a lexical variable, meaning it is restricted to its lexical scope. evaled code has its own lexical scope, so the surrounding code can't see the %hash1 declared inside the evaled code. One possible way to fix this issue would be to append some code to the string that you are evaling that returns the values, so for example something like my $hash1ref = eval($code."; return \\%hash1;");

    However, eval will execute arbitrary code, and if you use it for untrusted code, it might allow an attacker to inject any code into your system, which is of course a bad thing. One possible way around this is with my module Config::Perl, which will parse data structures like the one you showed. For example:

    use warnings; use strict; use Config::Perl; use Data::Dumper; my $parser = Config::Perl->new; my $data = $parser->parse_or_die('file.pl'); my %hash1 = %{ $data->{'%hash1'} }; print Dumper(\%hash1); __END__ $VAR1 = { 'hi' => 2, 'hello' => 1 };

    Another issue with your original code is that you need to give eval a string, not a filehandle. A simple way to slurp a file into memory is my $code = do { local $/; <$fh> };. Also, if you really want to use eval, you should check it for errors, in the above example by checking if $hash1ref is defined or not. Update: Also, instead of evaling a file, there's do. But as I said, I wouldn't recommend these methods.

      On top of all of those problems, the string eval will run at runtime, which is far too late for the strict 'vars' check to find out that %hash1 has been declared.
        the string eval will run at runtime, which is far too late for the strict 'vars' check to find out that %hash1 has been declared

        Did you mean "hasn't been declared"? If yes, then note that the strict check still kicks in, although as you said, at runtime. If you meant it as written, then this doesn't make sense to me... use strict 'vars'; has nothing to do with variables being declared twice (that's just a warning, in the shadow category, and it doesn't happen if the variables are in different scopes).

        $ perl -wMstrict -le 'eval "%hash1=(); 1" or warn "<<$@>>"' <<Global symbol "%hash1" requires explicit package name (did you forge +t to declare "my %hash1"?) at (eval 1) line 1. >> at -e line 1. $ perl -wMstrict -le 'my %hash1; my %hash1; print "Foo";' "my" variable %hash1 masks earlier declaration in same scope at -e lin +e 1. Foo $ perl -wMstrict -le 'my %hash1; { my %hash1; } print "Foo";' Foo $ perl -wMstrict -le 'my %hash1; eval "my %hash1;"; print "Foo";' Foo
Re: Reading a hash structure stored in a file
by davido (Cardinal) on Jan 16, 2019 at 15:57 UTC

    JSON, YAML, INI, TOML, XML... these are all serialization and data interchange formats. JSON, and YAML are particularly lightweight for representing datastructures such as hashes, arrays, and nested structures.

    Are you committed due to some immutable legacy technical debt to using Perl files to store your data? Could you possibly adopt a more secure, simpler format such as JSON? JSON is popular because it is simple, and if treated as data (ie, not evaled by a Javascript engine as code) is fairly secure, in that it won't be exposing your code to injected code.

    Perl supports JSON and YAML quite easily using modules from CPAN, and since Perl 5.14 JSON is even supported in the Perl core distribution via JSON::PP


    Dave

      It's interesting to see that JSON::PP is now the Perl core but I wonder why it doesn't seem to be listed as a core module on http://perldoc.perl.org. I wonder if there are other useful modules that are now core but have been omitted from the documentation.

      Cheers,

      JohnGG

Re: Reading a hash structure stored in a file
by karlgoethebier (Abbot) on Jan 16, 2019 at 12:58 UTC

    I guess what you really wanted is do. You may compare this with eval qx(cat $conf); as described in the manpage. A classic St. Larry mentioned in some ancient lost script many decades ago. But see also Config::Tiny and JSON::Tiny. Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Re: Reading a hash structure stored in a file
by kschwab (Vicar) on Jan 15, 2019 at 20:36 UTC

    The key advice is in the error: did you forget to declare "my %hash1"?

    So add "my %hash1;" either to your script, or the evaled file. Somewhere before assigning to it.

      So add "my %hash1;" either to your script, or the evaled file.

      Unfortunately that won't help in this case, since the file being evaled contains a my %hash1 declaration that'll mask a %hash1 declared in the main code.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1228615]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-04-25 09:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found