http://www.perlmonks.org?node_id=1016831


in reply to Re: Using grep and glob to find directories containing file
in thread Using grep and glob to find directories containing file

Thanks. Putting it in array context -- i.e. @{glob("$_/f*")} - did the trick. I changed the code to:
perl -wE 'use Data::Dumper;print Dumper grep(@{[glob("$_/f*")]},("dir1 +", "dir2", "dir3"))'
The code seems to work with that change but I just want to check that there is no need to explicitly check "if the list has at least one element" since presumably evaluating the list (by grep) will determine if it is empty or not.

I'm still not really sure why scalar context doesn't work (indeed, I would have thought scalar context would be better than list context). The stateful carry over part seems to be weird if not buggy. But as long as it works for me by forcing an array context, then it's all good even if it was far from obvious at first glance.

Replies are listed 'Best First'.
Re^3: Using grep and glob to find directories containing file
by moritz (Cardinal) on Feb 03, 2013 at 16:37 UTC
    I just want to check that there is no need to explicitly check "if the list has at least one element" since presumably evaluating the list (by grep) will determine if it is empty or not.

    Right, no need for an explicit check. Arrays in scalar context evaluate to the number of elements, so only empty arrays are false in boolean context.

    The stateful carry over part seems to be weird if not buggy.

    Well,

    glob</b> needs to carry state for this useful idiom to work: <code> while (my $file = glob '*.txt') { # do something with $file } </code> <p>Which is more friendly to memory than using a list and iterating th +at.</p> <p>But one could argue that <c>glob
    should discard its internal state when called with a different argument.

      Thanks for the help and explanation!!!

      This behavior is certainly not obvious and it is not (clearly) documented either under 'perldoc -f glob' or at perldoc.perl.org. The line saying "In scalar context, glob iterates through such filename expansions, returning undef when the list is exhausted" does not make it clear (at least to me) that such a state persists across calls to glob with a new argument! In fact, I can imagine all types of unintended errors when glob has been called in a scalar context in one place and then many lines later is called again in a different context and the programmer would have no idea that the state persists.

      But then again I am not a Perl monk so these things are never obvious to me :)
      I still think something is wrong...

      If I do something like:
      mkdir dir1 dir2 touch dir1/f{1,2}
      and then run:
      foreach (1,2,3,4) { print "dir1\n" if glob("dir1/f*"); print "dir2\n" if glob("dir2/f*"); }
      I get 'dir1' printed 3 times (NOT 4). But if I unwind the loop:
      print "dir1\n" if glob("dir1/f*"); print "dir2\n" if glob("dir2/f*"); print "dir1\n" if glob("dir1/f*"); print "dir2\n" if glob("dir2/f*"); print "dir1\n" if glob("dir1/f*"); print "dir2\n" if glob("dir2/f*"); print "dir1\n" if glob("dir1/f*"); print "dir2\n" if glob("dir2/f*");
      Then I get 'dir1' printed 4 times!!!

      In other words, I can understand why 'glob' is stateful when it is the loop iterator. But it makes no sense (to me) (and seems wrong) for it to be stateful when it is not the iterator. Because then unwinding a loop give a different answer.

        foreach (1,2,3,4) { print "dir1\n" if glob("dir1/f*"); print "dir2\n" if glob("dir2/f*"); }
        I get 'dir1' printed 3 times

        I would think your code would print 'dir1' twice. In your second code example, there is no advancing iterator because you can double the number of print statements and you will get double the output.

        Your first example doesn't tell the whole story. Look at this output:

        use strict; use warnings; use 5.012; for my $i (1..10) { print "$i: "; if (my $x = glob "dir1/f*") { print "\t$x"; } if (my $y = glob "dir2/f*") { print "\t$y"; } print "\n"; } --output:-- 1: dir1/f1 2: dir1/f2 3: 4: dir1/f1 5: dir1/f2 6: 7: dir1/f1 8: dir1/f2 9: 10: dir1/f1

        So it appears that inside a loop, once the iterator is exhausted, the next call to glob() returns undef, then the next call to glob() creates a new iterator(or resets the old iterator) and returns the first matching filename again.

        You can run a similar test on your second example:

        use strict; use warnings; use 5.012; if (my $x = glob "dir1/f*") { say $x; } if (my $x = glob "dir1/f*") { say $x; } if (my $x = glob "dir1/f*") { say $x; } if (my $x = glob "dir1/f*") { say $x; } if (my $x = glob"dir1/f*") { say $x; } --output:-- dir1/f1 dir1/f1 dir1/f1 dir1/f1 dir1/f1

        No loop, no call to the iterator's next() method. So as is often the case, perl examines the context of the statement to determine its return value.

Re^3: Using grep and glob to find directories containing file
by LanX (Saint) on Feb 03, 2013 at 23:45 UTC
    > grep(@{[glob("$_/f*")]}

    there are easier ways to count in list context,

    grep { () = <$_/*> } <dir{1,2,3}>

    (kind of a "half goatse" ;)

    Cheers Rolf

      Despite the admonition not to use <> for globbing?

      So...list context on the left, which then creates an anonymous array, which array, when evaluated in boolean/scalar context, produces the length of the array?

        > So...list context on the left, which then creates an anonymous array, which array, when evaluated in boolean/scalar context, produces the length of the array?

        No arrays involved, not even anonymous!

        It's just a list-assignment in scalar context.

        I would say much more performant than the former solution.

        Cheers Rolf

        PS:

        > Despite the admonition not to use <> for globbing?

        Source?

        Then I don't understand. If there's no anonymous array somewhere, then there's no counting going:

        use 5.012; my $x = ('a', 'b', 'c'); say $x; --output:-- 'c'
        Lists don't return their length in scalar context--only arrays return their length in scalar context. Although, now I see it has the same effect in your example--because in list context glob() returns an empty list when there are no matches, and trying to retrieve the last element of an empty list is undef or some other false value.