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

Hi,

I want to find out if "do there exist any *.test files in a certain directory" on a certain set of directories.

Given:     sub myglob { my @g = glob shift; @g }

What is the difference between these two statements:

if (glob "$dir/*") { print "glob $dir: yes\n"; } if (myglob "$dir/*") { print "myglob $dir: yes\n"; }
?

They both work the "first time" they are invoked, but if I have a loop to check more than one directory, 'glob' works at first EVERY OTHER call; here is a whole program:

#!/usr/bin/perl -w sub glob2 { glob(shift); } sub myglob { my @g = glob(shift); @g } use strict; use diagnostics; my @dirs = ('dir1', 'dir2', 'dir3', 'dir4', 'dir5', 'dir6'); #my @dir_x = '~'; my @dir_x = '/bin'; #my @dir_x = '/dir-fake-root'; my @fakedirs = ('dir-fake1', 'dir-fake2'); map { mkdir($_,0777) ; local *F; open(F,">$_/x.test") && close(F) || d +ie; } @dirs; for my $dir (@dirs, @fakedirs, @dir_x, @dirs, @fakedirs) { print "$dir/*: "; if (myglob("$dir/*")) { print "myglob "; } if (glob ("$dir/*")) { print "glob "; } if (glob2 ("$dir/*")) { print "glob2 "; } print "\n"; } map { unlink("$_/x.test") || die; rmdir($_) || die; } @dirs;

On both v5.6.1 cygwin and v5.005_03 linux, perl produces these results:

dir1/*: myglob glob glob2 dir2/*: myglob dir3/*: myglob glob glob2 dir4/*: myglob dir5/*: myglob glob glob2 dir6/*: myglob dir-fake1/*: dir-fake2/*: /bin/*: myglob glob glob2 dir1/*: myglob glob glob2 dir2/*: myglob glob glob2 dir3/*: myglob glob glob2 dir4/*: myglob glob glob2 dir5/*: myglob glob glob2 dir6/*: myglob glob glob2 dir-fake1/*: glob glob2 dir-fake2/*: glob glob2

As you can see, glob2 behaves like glob in all respects. myglob always works correctly. Before globbing /usr/*, glob correctly globs an existing directory every other call, and correctly doesn't find nonexistant directories.

After globbing /usr/*, glob then reports 'true' to every directory, even nonexistant ones (however, globbing /nonexistant-dir/* instead of /usr/* would kept glob working as it did the first time).

So what is the difference between the myglob defined here and regular glob? This is probably a caveat in perl scoping or contexts that I don't know about - I just learned perl last week to write a testsuite. And is there a better way to check for existance of any files in a given directory (opendir/readdir maybe)?

--thanks

Edit: chipmunk 2001-07-18

Replies are listed 'Best First'.
(tye)Re: glob weirdness / list contexts?
by tye (Sage) on Jul 18, 2001 at 09:51 UTC

    From perlop.pod (which is pointed to from "perldoc -f glob"):

    A (file)glob evaluates its (embedded) argument only when it is starting a new list. All values must be read before it will start over. In list context, this isn't important because you automatically get them all anyway. However, in scalar context the operator returns the next value each time it's called, or C run out[sic]. As with filehandle reads, an automatic defined is generated when the glob occurs in the test part of a while, because legal glob returns (e.g. a file called 0) would otherwise terminate the loop. Again, undef is returned only once. So if you're expecting a single value from a glob, it is much better to say     ($file) = <blurch*>; than     $file = <blurch*>; because the latter will alternate between returning a filename and returning false.
    So you have one case of using an array in a scalar context which returns the number of elements in the array which would be true if-and-only-if at least one file matched the glob. The other case is using glob in a scalar context which will return the "next matching file" from the first time glob was called (from that spot in your code) and then will return undef, ignoring the argument you pass in everytime except the first time and right after each time it returns undef.

    For a long time I've felt that "using glob in a scalar context" should have a warning associated with it. But backward compatibility gets in the way. /: (But it also used to be worse in that you could not replace the default glob and have it work in a scalar context.)

    When you use, in a scalar context, "an operation that would return a list if used in a list context", then you need to look up what that specific operations is going to return (and do) in that scalar context as there is no "standard" answer.

            - tye (but my friends call me "Tye")
Re: glob weirdness / list contexts?
by scain (Curate) on Jul 18, 2001 at 18:04 UTC
    giantfish,

    Two things: one, I had a similar question on the functioning of glob in a scalar context not to long ago. You may want to take a look at it Fileglob in a scalar context and More fileglob in a scalar context.

    As for better ways to do it, yes you could use opendir, readdir, and grep, and while it is not relevent to this case, you should take a look at the docs for the -X file test commands as they are very useful, and for some reason, often missed by programmers new to perl.

    Scott