I'm new to Perl and seem to have come across some behavior that I don't understand and was unexpected.
I'm trying to write a function that attempts to load a config file from a list of possible locations stored in an array. I'm using a foreach
loop to iterate over the array and check for the existence of the file, but after the first iteration, glob
is returning undef
, even when the value passed to it is a static, single-quoted string. Why?
Here is the code:
package MyPackage;
use warnings; use strict;
our @ConfigSearchPaths = (".", "~");
our $DefaultConfigName = ".my_config_file";
sub load_user_config
{
my ( $obj, $filename ) = @_;
$filename ||= $DefaultConfigName;
CONFIG_SEARCH: foreach my $search_path (@ConfigSearchPaths)
{
my $file_path = glob( "$search_path/$filename" );
# I added this line to test if the issue was related to interpolation
# but to my surprise, this also returns 'undef' after the first iteration.
my $file_path_2 = glob( '~/.my_config_file' );
if( -r $file_path )
{
# Parse the file...
}
}
}
The answer is in the documentation: see glob.
In scalar context,
glob
iterates through such filename expansions, returning undef when the list is exhausted.
In other words: in the first iteration, glob
returns the file itself; on the second iteration, it returns undef
, as there aren't any more matches.
By assigning to something that isn't an aggregate, you are using scalar context. ( See also: Scalar vs List Assignment Operator. )
Keep in mind that in scalar context, glob
does not observe the changes to $search_path
and instead iterates over the list results of the first call, using the non-interpolated string to "remember" the list being iterated.
If you don't want to iterate, use list context:
my @file_paths = glob "$search_path/$filename"; # Captures the whole array
# or
my ($file_path) = glob "$search_path/$filename"; # Discards all but the first item
I'd recommend reaching for Path::Tiny or a similar module to handle paths for you.