Search code examples
perlunixenvironment-variablesglob

Why does Perl's glob return undef for every other call?


I'm not necessarily looking for a better way to do this, rather an explanations of the output would greatly be appreciated. Recently, a senior programmer asked me why his code worked but only for one instance. What I came to find out was that it worked every other occurrence. Here is my example:

#!/usr/bin/perl -w
use strict;

my @list_env_vars = (
    '$SERVER',
    '$SERVER',
    '$SERVER',
    '$SERVER',
    '$SERVER',
    '$SERVER',
);

foreach (@list_env_vars){
    print "$_ = ".glob()."\n";
}

which output for perl 5.004:

$SERVER = UNIX_SERVER
$SERVER =
$SERVER = UNIX_SERVER
$SERVER =
$SERVER = UNIX_SERVER
$SERVER =

or output for perl 5.10:

$SITE = $SITE
Use of uninitialized value in concatenation (.) or string at glob_test.pl line 14.
$SITE =
$SITE = $SITE
Use of uninitialized value in concatenation (.) or string at glob_test.pl line 14.
$SITE =
$SITE = $SITE
Use of uninitialized value in concatenation (.) or string at glob_test.pl line 14.
$SITE =

I personally have never used glob() in this fashion so I was ill equipped to answer him. I read through perldoc glob documentation and followed the File::Glob link on that page and still couldn’t find anything that would explain the output. Any help would be much appreciated.


Solution

  • glob in scalar context:

    In scalar context, glob iterates through such filename expansions, returning undef when the list is exhausted.

    In

    foreach (@list_env_vars){
        print "$_ = ".glob()."\n";
    }
    

    The glob() there really is glob($_). Every iteration, $_ contains the string $SERVER. Given that the environment variable does not change, $SERVER is expanded to the same string. First time, this string is returned. Next, the list is exhausted, so undef is returned. Third time, we start over. ...

    Clarification: It does not matter that the argument to the second call is the same as the one for the first call since there is no way to reset glob's iterator.

    You can see this more clearly using the following example (current directory contains files '1.a', 1.b', '2.a' and '2.b'):

    #!/usr/bin/perl -w
    use strict;
    
    my @patterns = (
        '*.a',
        '*.b',
    );
    
    for my $v ( @patterns ) {
        print "$v = ", scalar glob($v), "\n";
    }
    

    Output:

    C:\Temp> d
    *.a = 1.a
    *.b = 2.a
    

    I would recommend accessing environment variables via the %ENV hash:

    my @list_env_vars = ($ENV{SERVER}) x 6;
    

    or

    my @list_env_vars = @ENV{qw(HOME TEMP SERVER)};