-1

I have a read-only perl file with a huge hash defined in it. Is there anyway for me to read this perl file and dump out the hash contents?

this is basic structure of the hash within the file.

%hash_name = {
    -files => [
         '<some_path>',
    ],
    -dirs => [
         '<some_path>',
         '<some_path>',
         '<some_path>',
         '<some_path>',
         '<some_path>',
    ],
};
newbie
  • 1
  • 2
  • can you not cat the file and redirect it into a one that does have write permissions? `cat perl_file_name > new_perl_file_name` – Davy M Nov 11 '17 at 00:30
  • yes I did consider that but will go with that approach only if there is no other way to dump the hash without creating a new file. – newbie Nov 11 '17 at 00:34
  • Is the entire hash found between parenthesis, and if so, are they the only parenthesis in the file? Is it a hash that starts off with `qw(` or is it just `(` with the `=>` symbols inside? Or is the hash defined one element at a time with the `$hash_name{'key'} = 'value';` setup? – Davy M Nov 11 '17 at 00:43
  • What is this "_perl file_" -- a file ending in `.pl` with Perl code, and you want just that one variable (hash) from it? Or is it a file that has only that hash? – zdim Nov 11 '17 at 01:10
  • I have included the format of the hash – newbie Nov 11 '17 at 01:14
  • 3
    @newbie Thank you, and to repeat the question: Does this file have other Perl code or just this hash? Also, is the hash undeclared (just `%hash_name`), as you show it, or is it "lexical," so with `my` such as: `my %hash_name`? – zdim Nov 11 '17 at 02:59
  • 2
    @newbie What you show is invalid in Perl: the `%` in `%hash_name` indicates that the variable is a _hash_, but `{ .. }` form a _hash reference_, which is a _scalar_ variable (not a hash). So it should be either `%hash_name = ( .. )` or it's `$hashref_name = { .. }` – zdim Nov 11 '17 at 03:12
  • 1
    Note this is an insecure way to store data. The data file must be evaluated as perl code. Any arbitrary code could be in the file. In addition, the data file can only be read by Perl programs. Instead, use JSON or similar data format. [JSON::MaybeXS](https://metacpan.org/pod/JSON::MaybeXS) can convert between JSON and Perl. – Schwern Nov 11 '17 at 06:59

1 Answers1

2

Ideally you'd copy the file so that you can edit it, then turn it into a module so to use it nicely.

But if for some reason this isn't feasible here are your options.

If that hash is the only thing in the file, "load" it using do and assign to a hash

use warnings;
use strict;

my $file = './read_this.pl';  # the file has *only* that one hash

my %hash = do $file;

This form of do executes the file (runs it as a script), returning the last expression that is evaluated. With only the hash in the file that last expression is the hash definition, precisely what you need.

If the hash is undeclared, so a global variable (or declared with our), then declare as our a hash with the same name in your program and again load the file with do

our %hash_name;  # same name as in the file
do $file;        # file has "%hash" or "our %hash" (not "my %hash")

Here we "pick up" the hash that is evaluated as do runs the file by virtues of our

If the hash is "lexical", declared as my %hash (as it should be!) ... well, this is bad. Then you need to parse the text of the file so to extract lines with the hash. This is in general very hard to do, as it amounts to parsing Perl. (A hash can be built using map, returned from a sub as a reference or a flat list ...) Once that is done you eval the variable which contains the text defining that hash.

However, if you know how the hash is built, as you imply, with no () anywhere inside

use warnings; 
use strict;

my $file = './read_this.pl';

my $content = do {  # "slurp" the file -- read it into a variable
    local $/;
    open my $fh, '<', $file or die "Can't open $file: $!";
    <$fh>;
};

my ($hash_text) = $content =~ /\%hash_name\s*=\s*(\(.*?\)/s;
my %hash = eval $hash_text;

This simple shot leaves out a lot, assuming squarely that the hash is as shown. Also note that this form of eval carries real and serious security risks.


  Files are also loaded using require. Apart from it doing a lot more than do, the important thing here is that even if it runs multiple times require still loads that file only once. This matters for modules in the first place, which shouldn't be loaded multiple times, and use indeed uses require.

On the other hand, do does it every time, what makes it suitable for loading files to be used as data, which presumably should be read every time. This is the recommended method. Note that require itself uses do to actually load the file.

Thanks to Schwern for a comment.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • `do` will always load the file. `require` will only load it once. Since you want to get data from the file, it's recommended to use `do`. Else the second or third time anything in that process loads the file they'll end up with `1`. – Schwern Nov 11 '17 at 04:31
  • @Schwern Right, thank you for the comment. I wanted to avoid excessive explanation thus I simply use `do`. (I still mention `require` since it is feasible that the data _is_ loaded once.) But it is good to state this, thank you -- I am adding the comment. – zdim Nov 11 '17 at 04:41
  • It's bad practice to use `require` because a future person maintaining the code may also `require` the same file elsewhere (not even in the same code file, it's per process) and not realize it has already been required. I'd suggest instead explaining why `do` is the right thing to do here instead of `require`, it's a necessary complexity. – Schwern Nov 11 '17 at 04:57
  • I wish I could give this two upvotes, one for the great answer, and the other for including in your solutions the `use warnings; use strict;` since so many beginning programmers who will look at this will see that it's important to have those on always, I see lots of beginning perl programmers who don't put those and then ask questions about issues that would have been simply prevented by including warnings and strict. Overall, a really great and thorough answer! – Davy M Nov 12 '17 at 20:51
  • 1
    @DavyM Thank you for kind words. You are right, and there are so many such questions that it even seems _more_ common (than otherwise) when people start out. All that we can do is to keep pointing it out, and write it always in full code examples. – zdim Nov 13 '17 at 02:15