Perl glob strange behaviour

2020-04-10 02:54发布

I have a piece of perl code:

if (glob("$data_dir/*$archivefrom*")) {
    my $command1 = "zip -r -T -m $backup_dir/$archivefrom.zip $data_dir/*$archivefrom*";

    my $err_cmd1 =system("$command1");
    if ($err_cmd1 != 0){print "Error $command1\n";exit 1;}
}

sometimes the if returns true but the zip would not match anything, why would that happen? There is no concurrent processes that would remove files in the meantime, it's just glob is returning something different from zip archive match for files, it returns non-empty result even though it should be empty.

标签: perl glob
1条回答
够拽才男人
2楼-- · 2020-04-10 03:44

The glob function in scalar context becomes an iterator, not a test for file existence! This can be demonstrated by the following code:

use feature 'say';
my @patterns = ('{1,2,3}', 'a', 'b', 'c');
for my $pattern (@patterns) {
  my $item = glob $pattern;
  say $item // "<undef>";
}

Output:

1
2
3
<undef>

That is, glob remembers the first pattern it was given, then iterates over all matches, but not use any new patterns it was given until the iterator is exhausted. Therefore in your expression glob("$data_dir/*$archivefrom*"), updated values in the $data_dir and $archivefrom variables are not recognized. You will also get one undef return value at the end of all possibilities. Then the if branch obviously isn't executed.

To test if at least one file matching your pattern exists, you have to fetch all matches at once, thus avoiding the iteration. Use list context for that. We could use the pseudoperator ()= to count the number of matches – anything ≥ 1 is fine. We can also assign the first match to a variable, and use it in your system command, to avoid shell interpolation issues:

use autodie ':system'; # automatic error handling, additionally requires IPC::System::Simple

if (my ($archivePath) = glob "$data_dir/*$archiveFrom*") {
  system 'zip', '-r', '-T', '-m', "$backup_dir/$archivefrom.zip", $archivePath;
}
查看更多
登录 后发表回答