http://qs321.pair.com?node_id=575380

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

Fellow Monks,
I must confess I am confused.

In one of my programs I concatenate some user-provided strings to build a file-match pattern which I then feed to bsd_glob from the File::Glob module. Then I need to detect whether it matched some files and proceed doing something with them. Sounds quite straightforward to me. But during this I ran into a strange runtime behaviour that I could boil down to the following test script:

#! /usr/bin/perl use strict; use warnings; use Data::Dumper; use File::Glob qw/:glob/; # There lives a file "passwords.dat" in the current directory.... # This matches the whole filename. OK my $pat1 = "passwords.dat"; print "\nPATT $pat1\n"; print "PRE ERR: ", Dumper($!); my @list = bsd_glob($pat1, GLOB_ERR); print Dumper(\@list); print "POST ERR: ", Dumper($!); print "\n"; # This file is not there so it doesn't match and afterwards $! ist # set. OK my $pat2 = "this_will_miss_in_globbing"; print "\nPATT $pat2\n"; print "PRE ERR: ", Dumper($!); @list = bsd_glob($pat2, GLOB_ERR); print Dumper(\@list); print "POST ERR: ", Dumper($!); print "\n"; # This should match again since it is just the same as the first match # but *afterwards $! ist still set*!!?? my $pat3 = "passwords.dat"; print "\nPATT $pat3\n"; print "PRE ERR: ", Dumper($!); @list = bsd_glob($pat3, GLOB_ERR); print Dumper(\@list); print "POST ERR: ", Dumper($!); print "\n"; # This resets $! back to normal which is just fine my $pat4 = "*.dat"; print "\nPATT $pat4\n"; print "PRE ERR: ", Dumper($!); @list = bsd_glob($pat4, GLOB_ERR); print Dumper(\@list); print "POST ERR: ", Dumper($!); print "\n";
Using GLOB_ERR or GLOB_NOCHECK or both of them changes nothing. Now my questions are I assume that the error is on my side since this module is a core module which is even used to implement Perls glob() built-in.

Regards... stefan k
you begin bashing the string with a +42 regexp of confusion

Replies are listed 'Best First'.
Re: bsd_glob does not reset $!
by merlyn (Sage) on Sep 28, 2006 at 16:19 UTC
    I'm not sure if it's documented except indirectly but $! is never supposed to be reset on success, only set on failure. That's because it mimics the underlying errno variable in libc, and that's the way it works.

    Thus, never look at $! unless you know the thing you just did caused an error from which you need details.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: bsd_glob does not reset $!
by ikegami (Patriarch) on Sep 28, 2006 at 16:23 UTC

    It's stated very clearly in File::Glob.

    If an error occurred, &File::Glob::GLOB_ERROR will be non-zero and $! will be set. &File::Glob::GLOB_ERROR is guaranteed to be zero if no error occurred

    perlvar has more to add on $!. (Emphasis in the original.)

    This means that the value of $! is meaningful only immediately after a failure. [...] A successful system or library call does not set the variable to zero.

    There's even an example in File::Glob's Synopsis. (The example was adjusted to your case.)

    @list = bsd_glob($pat, GLOB_ERR); if (GLOB_ERROR) { # an error occurred }
      Hi all,
      a few notes on the replies
      • GLOB_ERROR ... Of course I read the perldoc and I tried to use GLOB_ERROR. Unfortunately it is not set to a value other than zero when the globbing produced no result. When I introduce a print Dumper(GLOB_ERROR) after each call to bsd_glob it shows '0' in every case.
      • $! is not reset ... Right, it would be quite counterintuitive if it were reset. That was a stupid assumption of mine. But then, why is it reset with $pat4 in the above example??
      • As to chromatics question: Nah, I may be stupid but it's not that bad ;-) This is not real world code it's just me trying to boil the problem down to some small example. You are right, that I should have swapped the lines dumping the list and dumping $!
      I am still trying to get this working. Any more pointers?

      Following an old rule o' thumb: there must be something very dumb in my thinking or in my code, taking into account the amount of time I have already spent with this problem ;-)

      Regards... stefan k
      you begin bashing the string with a +42 regexp of confusion

        Unfortunately it is not set to a value other than zero when the globbing produced no result

        No matches is not an error. You could check for that condition explicitely.

        if (GLOB_ERROR || !@list) { ... error or no match ... }

        But then, why is it reset with $pat4 in the above example??

        The value of $! is meaningless on success. It could be zero, non-zero, undef, etc.

Re: bsd_glob does not reset $!
by cdarke (Prior) on Sep 28, 2006 at 16:23 UTC
    I can't see the error you are getting, but, from perlvar $!: "A successful system or library call does not set the variable to zero."
    This is consistent with C system calls.
Re: bsd_glob does not reset $!
by chromatic (Archbishop) on Sep 28, 2006 at 20:56 UTC

    Whoa, is this your real code?

    print Dumper(\@list); print "POST ERR: ", Dumper($!);

    The first print causes system calls too, does it not?

Re: bsd_glob does not reset $! (The lessons learned)
by stefan k (Curate) on Sep 29, 2006 at 09:31 UTC
    Hi,
    thanks to the help from the CB I have collected some answers.
    • I am still confused about the way $! behaves in this case, but I am not supposed to understand it (unless I learn the internals of Perl) and I am only supposed to use it when any error was signaled.
    • The desired outcome of bsd_glob can be achieved by passing the flag !GLOB_NOCHECK. Then the returned list will not contain the search pattern upon unsuccessful globbing. In my case this was hard because the real-world application in question sometimes uses a full pathname as a search pattern.
    • For performance and sanity my routine will first do a test for file-existance and then try to glob it. It feels clumsy, though.
    • Another way to check the outcome would be to undef $! before calling bsd_glob but since the documentation does not explicitly state the observed behaviour I will not rely on it to work.
    I think this sums it up.

    Thanks

    Regards... stefan k
    you begin bashing the string with a +42 regexp of confusion

      For performance and sanity my routine will first do a test for file-existance and then try to glob it. It feels clumsy, though.

      You need to check for file-existance afterwards, not beforehand. glob is not guaranteed to produce a list of existing files. For example, glob('file{a,b}') will return filea and fileb whether either, neither or both exists.

      @list = bsd_glob('~gnat', GLOB_ERR); die("Unable to glob: $!\n") if GLOB_ERROR; # Keep only references to existing files. @list = grep -f, @list; die("glob returned no existing results\n") if not @list;

      I am still confused about the way $! behaves in this case

      This case is not special.

      # XXX WRONG $rv = print $fh (...); if ($!) { die("Unable to write to file: $!\n") } # Ok $rv = print $fh (...); if ($rv) { die("Unable to write to file: $!\n") }