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

Hi Monks,
I'm perplexed about the workings of File::DosGlob.

In the Windows folder in which this Perl program lives there are two files. Their names here are simple but in the real program I need to allow for spaces to exist in the filenames. My problem arises in that, within a loop, when I use File::DosGlob::glob to generate a filename, the '-f' test I do against the file only works on the first file in the list of files I'm testing. When I reverse the order of the files I test, it again approves of the first in the list but balks at the second.

And to point out the obvious, when I run the glob in a different statement block, it happily accepts a filename that it previously just rejected.

Any ideas of what's happening? Thanks.

use strict; use File::DosGlob 'glob'; my @files_to_check = ( 'file_a.txt', 'file_b.txt' ); FILE_IN_ALPHA_ORDER: foreach my $template_file ( sort @files_to_check ) { # The next three lines are for peeking at $template_file warn "ftf2: '$template_file'\n"; my $template_file_t2 = qq{"$template_file"}; warn "ftf2a: '$template_file_t2'\n"; unless ( -f ( File::DosGlob::glob qq{"$template_file"} ) ) { warn "8251b: File check, cannot access '$template_file', $!\n\ +n"; next FILE_IN_ALPHA_ORDER; } warn "ftf2b: '$template_file' is okay.\n\n"; } warn "\n"; FILE_IN_REVERSE_ORDER: foreach my $template_file ( reverse sort @files_to_check ) { # The next three lines are for peeking at $template_file warn "ftf3: '$template_file'\n"; my $template_file_t2 = qq{"$template_file"}; warn "ftf3a: '$template_file_t2'\n"; unless ( -f ( File::DosGlob::glob qq{"$template_file"} ) ) { warn "8251b: File check, cannot access '$template_file', $!\n\ +n"; next FILE_IN_REVERSE_ORDER; } warn "ftf2b: '$template_file' is okay.\n\n"; } __END__ Results look like: ftf2: 'file_a.txt' ftf2a: '"file_a.txt"' ftf2b: 'file_a.txt' is okay. ftf2: 'file_b.txt' ftf2a: '"file_b.txt"' 8251b: File check, cannot access 'file_b.txt', No such file or directo +ry ftf3: 'file_b.txt' ftf3a: '"file_b.txt"' ftf2b: 'file_b.txt' is okay. ftf3: 'file_a.txt' ftf3a: '"file_a.txt"' 8251b: File check, cannot access 'file_a.txt', No such file or directo +ry

Replies are listed 'Best First'.
Re: Using File::DosGlob::glob in loop only works first time
by ikegami (Patriarch) on Feb 24, 2006 at 18:32 UTC

    When using glob in a scalar context, you must loop until it returns undef. Compare

    use strict; use warnings; use File::DosGlob 'glob'; $| = 1; for (1..2) { my $glob = glob($0); print($glob, "\n"); } __END__ script.pl Use of uninitialized value in print at script.pl line 11.

    with

    use strict; use warnings; use File::DosGlob 'glob'; $| = 1; for (1..2) { while (defined(my $glob = glob($0))) { print($glob, "\n"); } } __END__ script.pl script.pl

    That said, I'm not sure why you're using glob at all if you only look at the first element. Why not just do unless ( -f  $template_file )?

      That said, I'm not sure why you're using glob at all if you only look at the first element. Why not just do unless ( -f $template_file )?

      I have a list of files that I want to ensure are actually files (the user will be feeding in filenames via a configuration file and I don't want the program to go further if said file(s) don't exist). I'm using glob in case the absolute path names contain a space character somewhere.

      The thing that's bothering me about this is "Why does glob give me a valid answer for the first item in my list, even when I'm using it in scalar context, and not the second?" Is it set in stone somehow, like m/$abc/o is set to only optimize the first interpretation you ask of it? Because in my example, when I use a different instance of glob (i.e. the second foreach loop/block) against the "second" filename, it kicks it out just fine (and then fails on the "previously okay" first value). In DWIM-land, I'd think that each invocation of glob, even within a foreach loop, would be independent of the last.

      Though as I said before, I can move ahead with answers that work, and the slice/[0] suggestion works. :-)

        I'm using glob in case the absolute path names contain a space character somewhere.

        How does glob help you with path names that have spaces? glob accepts a file spec (i.e. something with *) and returns all matching files. It's used to allow the user to specify multiple files at once. However, since you're only interested in one file, it makes no sense to accept file specs or to use glob.

        -f works with spaces, whether you've passed the value through glob or not.

        The thing that's bothering me about this is "Why does glob give me a valid answer for the first item in my list, even when I'm using it in scalar context, and not the second?"

        When glob is called in a scalar context, it still returns all the values it would in a list context. It just returns one by one. Somehow, it has to tell you when it's done returning all the values. It does that by returning undef.

        The first time you call glob, it returns the first filename that matches the file spec.

        The second time you call glob, it returns undef since there are are no files that match the file spec passed to the first call to glob.

Re: Using File::DosGlob::glob in loop only works first time
by japhy (Canon) on Feb 24, 2006 at 17:46 UTC
    Using glob in scalar context is a bad idea. Is there a reason you're doing this?

    The quick fix is to do -f (File::DosGlob::glob qq{"$template_file"})[0] which puts the glob into list context.


    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      No reason other than uninformed DWIMmery. :-)

      Thanks, your suggestion works perfectly in my real code. Changing the first line here (and leaving the second alone) to:

      unless ( -f ( File::DosGlob::glob qq{"$template_file"} )[0] ) +{
      produces:

      ftf2: 'file_a.txt' ftf2a: '"file_a.txt"' ftf2b: 'file_a.txt' is okay. ftf2: 'file_b.txt' ftf2a: '"file_b.txt"' ftf2b: 'file_b.txt' is okay. ftf3: 'file_b.txt' ftf3a: '"file_b.txt"' ftf2b: 'file_b.txt' is okay. ftf3: 'file_a.txt' ftf3a: '"file_a.txt"' 8251b: File check, cannot access 'file_a.txt', No such file or directo +ry
        I guess my more important question is: why are you using glob()? Are these fileglobs or filenames? Why are you using glob() on something that's not a glob? Is it for some evil windows reason?

        Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
        How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: Using File::DosGlob::glob in loop only works first time
by Sandy (Curate) on Feb 25, 2006 at 02:12 UTC
    The following tests were done on Solaris, using just the standard 'glob' as opposed to DosGlob.

    First, my directories

    > ls -1 abc.txt def.txt xyz.boo
    Now, use glob in scalar context, and it is apparent that it buffers it's output, to be sent back one at a time, regardless if it is called again with a new parameter.
    > perl -e '@a=qw(*.txt xyz.boo);foreach $f (@a) {print "$f->";$y=glob( +$f);print $y,"\n";}' *.txt->abc.txt xyz.boo->def.txt
    Do the same thing in list mode, and the list is 'refreshed' when a new glob is called.
    > perl -e '@a=qw(*.txt xyz.boo);foreach $f (@a) {print "$f->";($y)=glo +b($f);print $y,"\n";}' *.txt->abc.txt xyz.boo->xyz.boo
    That said, you don't really need to use glob unless you use wildcards.

    I got bit once because I mistakenly believed that glob would always return files that only matched the inputs (with or without wildcards). In other words, the following is only true IF you use wildcards.

    The first time you call glob, it returns the first filename that matches the file spec.

    If there are no wildcards in the parameter passed to glob, glob will simply return the string, even if the file does not exist. Notice that 'boo.txt' is not a valid file.
    > ls -1 abc.txt def.txt xyz.boo > perl -e '($y)=glob("*.txt");print $y,"\n";' abc.txt > perl -e '($y)=glob("boo.txt");print $y,"\n";' boo.txt