http://www.perlmonks.org?node_id=1002907

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

I've done some searching and found printing the variable name and value is trivial if you know at least the variable names you want to operate on. I have a script that creates a user shell where the user can create variables and then use them later. How can I print a list of all the variable names/values the user has defined?

Here's the script, I purposely don't 'use strict' so the user doesn't have to type 'my' to create variables and so they are accessible for the duration of the script:

#use strict; use warnings; use Term::ReadLine; my $prompt = "prompt> "; my $prog = Term::ReadLine->new('Script'); $prog->ornaments(0); while (defined (my $cmd = $prog->readline($prompt))) { chomp $cmd; # nothing if ($cmd =~ /^\s*$/) { next } # exit if ($cmd =~ /^\s*exit\s*$/) { last } # execute eval $cmd; warn $@ if $@; print "\n" } sub command { print "Special command"; }

And here is basic operation:

VinsWorldcom@C:\Users\VinsWorldcom\tmp> perl script.pl prompt> command Special command prompt> $var1 = "variable" prompt> print $var1 variable prompt> $var1 = "variable1" prompt> print $var1 variable1 prompt> exit VinsWorldcom@C:\Users\VinsWorldcom\tmp>

The above simple example shows calling an "internal command" and defining a variable ($var1), printing it, redefining it and printing again. Not too complicated.

Suppose the user creates 100 variables and changes the values of them over and over again and then gets confused. It would be nice to supply an "internal command" (like the 'sub commmand()' in the example script) that would print all the variable names/values the user has created. This is complicated (for me at least) since all the solutions I've found presuppose the variable names are known - in this, case they are not known; created by the user during script run-time.

Of course, they must be 'stored' somewhere since subsequent "prompt>" allows the user to print previously defined variables (if the user can remember the name of the variable), but where are they stored? And how to access them?


SOLVED: Thanks to Athanasius recommendation below, I used that advice to create the following:

package MyPackage; ... sub variables { my %SKIP = ( # list var names that you want to ignore ); for my $var (sort(keys(%MyPackage::))) { if (defined(${$var}) && !defined($SKIP{$var})) { print "\$$var = ${$var}\n" } } }

That was MUCH easier than I thought and honestly thought I had tried something similar which didn't work which lead to my searches and finally this question. I feel better with a Monk-certified solution - THANKS Athanasius ++ and to all other respondents.

Replies are listed 'Best First'.
Re: Print variable name/value for all program variables
by greengaroo (Hermit) on Nov 08, 2012 at 14:35 UTC

    After a quick Google search, I found this interesting thread on Stack Overflow: How can I list all variables that are in a given scope?

    Take a look at this code, from the thread:

    { no strict 'refs'; foreach my $entry ( keys %main:: ) { print "$entry\n"; } }

    Although, I don't think you can use this "as is", I think this is a good clue for what to look for.

    Testing never proves the absence of faults, it only shows their presence.

      Yes. Include a package declaration at the head of your script, then you can get the names of the variables (and subroutines) as follows:

      0:51 >perl -E "package Foo; $x = 42; $y = 'fred'; sub command { print + 'Special command'; } say for sort keys %Foo::;" command x y 0:54 >

      Hope that helps,

      Athanasius <°(((><contra mundum

Re: Print variable name/value for all program variables
by kennethk (Abbot) on Nov 08, 2012 at 15:43 UTC
    So I'll first point out what I'm about to give will not help with lexical variables, since they have no entry in the symbol table. However, and thankfully, lexical variables can't escape the scope of your eval, so they won't persist and thus shouldn't be an issue.

    I'll also assume that you won't be dealing with people changing packages. Otherwise, you'll have to navigate around all possible packages and keep track of an initial state.

    Oh, and string eval, security risk, blah, blah.

    Lastly, this solution will fail if a scalar actually holds an undef. This is because *{$glob}{SCALAR} will always return a reference to a scalar regardless of whether you ever actually accessed that scalar. The docs say "[t]his might change in a future release."

    I give you my modified version of your code:

    #use strict; use warnings; use Term::ReadLine; my $prompt = "prompt> "; my $prog = Term::ReadLine->new('Script'); $prog->ornaments(0); while (defined (my $cmd = $prog->readline($prompt))) { chomp $cmd; # nothing if ($cmd =~ /^\s*$/) { next } # exit if ($cmd =~ /^\s*exit\s*$/) { last } # execute eval "package Sandbox; $cmd"; warn $@ if $@; print "\n" } package Sandbox; sub command { print "Special command"; } sub names { my @keys = sort keys %Sandbox::; for my $key (@keys) { my $glob = \*{"Sandbox::$key"}; print "\$$key\n" if defined ${*{$glob}{SCALAR}}; print "\@$key\n" if defined *{$glob}{ARRAY}; print "\%$key\n" if defined *{$glob}{HASH}; } }

    Outside of the addition of the names routine, you'll note I've added package statements to your eval and the subroutine declarations. This avoids the problem of your dump being polluted by the very large number of special variables in the main package.

    This approach also has the advantage of only outputting names associated with actual variables, as well as telling the user what variable type they were. Just dumping the names from the table will also yield all subroutines in the package, which is probably not what you mean. You could even add a subroutine to print out all available subroutines:

    sub commands { my @keys = sort keys %Sandbox::; for my $key (@keys) { my $glob = \*{"Sandbox::$key"}; print "\&$key\n" if defined *{$glob}{CODE}; } }

    Reference materials to be found in Symbol Tables from perlmod and in perlref.


    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Print variable name/value for all program variables
by clueless newbie (Curate) on Nov 08, 2012 at 14:39 UTC

    Devel::Symdump has scalar, array and hash methods for returning variables of those types.