Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 865
Akers Avatar asked Aug 13 '09 21:08

Akers


2 Answers

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)};
like image 169
Sinan Ünür Avatar answered Sep 19 '22 10:09

Sinan Ünür


Incidentally, the reason why in 5.004 you get a variable expansion, while on 5.10 you just get your literal string back, is because on old perl, glob() was carried out by the system shell, which just as a side-effect performs variable expansion. Since perl 5.6, glob() uses the File::Glob module which does the work itself, without the shell, and doesn't expand environment variables (which glob was never intended to do). %ENV is the proper way to get at the environment.

like image 43
hobbs Avatar answered Sep 22 '22 10:09

hobbs