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

On p390 of the Camel, 4th edition, there is a code example for how to peek into the main:: symbol table hash ("stash"). I sort of duplicated it in my code, below (with changes to names of variables to suit my tastes), but I get a compile time error. The error is, precisely:

Variable "@sym" is not imported at Scripts-unfinished/env-paths-2yaml.pm line 23. Variable "%sym" is not imported at Scripts-unfinished/env-paths-2yaml.pm line 24. Global symbol "@sym" requires explicit package name (did you forget to declare "my @sym"?) at Scripts-unfinished/env-paths-2yaml.pm line 23. Global symbol "%sym" requires explicit package name (did you forget to declare "my %sym"?) at Scripts-unfinished/env-paths-2yaml.pm line 24. Execution of Scripts-unfinished/env-paths-2yaml.pm aborted due to compilation errors.

Here's the code at the present moment (how much you want to wager that 5 minutes from now I'll see what's wrong? ;-):

#!/usr/bin/env perl # First created: Thu Jul 24 2025 12:47:02 PM -04:00 [EDT] use strict; use v5.18; use utf8; use warnings; use Data::Dumper qw/Dumper/; # use Perl::ImportReport; use Env qw/MANPATH INFOPATH PATH/; # ---------------------- ### ---------------------- # # BEGIN { # require Env; Env->import( grep( /[A-Z]*PATH/ , keys %ENV ) ); # } # ---------------------- ### ---------------------- # my ($symname); use vars ('$sym'); foreach $symname ( sort keys %main::Env:: ) { local *sym = $main::Env::{$symname}; say "\@$symname array is populated" if @sym; say "\%$symname hash is populated" if %sym; } # Try with ImportReport - doesn't work ### -------------------- ### ----------------------- # # my $object = Perl::ImportReport->new( $INC{'Env.pm'} ) # || die "Invalid value for PPI document source"; # say Dumper( $object->get_import_report('imports') ); ### -------------------- ### ----------------------- # __END__

    - Soren

Jul 24, 2025 at 18:17 UTC

A just machine to make big decisions
Programmed by fellows (and gals) with compassion and vision
We'll be clean when their work is done
We'll be eternally free yes, and eternally young
Donald Fagen —> I.G.Y.
(Slightly modified for inclusiveness)

Replies are listed 'Best First'.
Re: Interrogating stashes - Camel example doesn't work?
by stevieb (Canon) on Jul 24, 2025 at 19:10 UTC

    You're close, but you have to dereference the symbol table entry appropriately, not use it directly as a local variable. The array/hash doesn't get magically created:

    use strict; use warnings; use feature 'say'; for my $symname (sort keys %main::) { local *sym = $main::{$symname}; say "\@$symname array is populated" if @{ *sym }; # <-- HERE say "\%$symname hash is populated" if %{ *sym }; # <-- and HERE }

    Output:

    %CORE:: hash is populated %Carp:: hash is populated %DB:: hash is populated %DynaLoader:: hash is populated %ENV hash is populated @INC array is populated %INC hash is populated %IO:: hash is populated %Internals:: hash is populated %PerlIO:: hash is populated %Regexp:: hash is populated %Tie:: hash is populated %UNIVERSAL:: hash is populated %builtin:: hash is populated %constant:: hash is populated %feature:: hash is populated %main:: hash is populated %mro:: hash is populated %re:: hash is populated %strict:: hash is populated %utf8:: hash is populated %version:: hash is populated %warnings:: hash is populated

    Here's a bit of an example using the actual entries:

    use strict; use warnings; use feature 'say'; for my $symname (sort keys %main::) { local *sym = $main::{$symname}; # Make a copy of the symtab entry if (@{ *sym }) { my @array = @{ *sym }; print "$_\n" for @array; } # Use the symtab entry directly if (%{ *sym }) { for my $key (keys %{ *sym }) { print "$key: ${ *sym }{$key}\n"; } } }

    Disclaimer: Please note that using the symbol table directly can be extremely dangerous. You can cause slight or even major problems in very far away code that can be a nightmare or even impossible to track down. This is a very relevant read. It doesn't go into symbol table use directly, but the premise is exactly the same. I find all three parts to be an extremely entertaining and funny read, and it's always nice every couple of years when I have to reference it, as I re-read it each time.

    --stevieb

      Sometimes I'm one of the people who says to use a hash instead, and sometimes I'm one of the people who answers the question that was asked, even though I think they should be using a hash instead.

      Classic MJD! 😄 (see XY Problem for more examples)

      stevieb began his expository goodness with:

      You're close, but you have to dereference the symbol table entry appropriately, not use it directly as a local variable. The array/hash doesn't get magically created

      I see! I wonder if something has changed in how Perl parses such things, since, as I mentioned, the example was virtually verbatim from The Divine Book of All Things Perlish (the "Camel"). Whatever the case, your assistance caused me to become enlightened and was very appreciated. I've incorporated this dereferencing technique in code in a module I'm working on.

          - Soren

      Jul 28, 2025 at 00:42 UTC

        You are getting different results, because you are using strict & warnings -- neither of which are enabled in the original sample code in the book....

        foreach $symname (sort keys %main::) { local *sym = $main::{$symname}; print "\$$symname is defined\n" if defined $sym; print "\@$symname is nonnull\n" if @sym; print "\%$symname is nonnull\n" if %sym; }
        the example was virtually verbatim from [the Camel Book]

        Perhaps it would be a good exercise to try it actually verbatim? That way you will be enlightened as to whether something has indeed changed or if it was just your tweaks which have caused the problem.


        🦛

Re: Interrogating stashes - Camel example doesn't work?
by ysth (Canon) on Jul 27, 2025 at 03:14 UTC
    The "*foo{THING}" notation is used to get a reference to a slot of a typeglob or undef if that slot is unused (except that it creates a scalar if one doesn't exist).

    You want:
    ... if *sym{ARRAY}; ... if *sym{HASH};
    or *{ $main::Env::{$symname} }{ARRAY} or even just *{ $Env::{$symname} }{ARRAY}.

      ysth wrote:

      The "*foo{THING}" notation is used to get a reference to a slot of a typeglob (...)

      I'm glad you added this knowledge to the thread, ysth, it's an important even if rather rarely used notation (I think). I remembered having applied myself to study of this "*foo{THING}" technique in the dim past. Thanks!

          - Soren

      Jul 28, 2025 at 00:54 UTC