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

I have a directory with data files having longish, informative names; part of each file name is a unique numeric identifier (other parts are date_time, file type). I want to take a list of chosen numeric ID values, and look up the full path/file name that matches each item in the list.

So I use a file glob to match each ID value, but only half of the list elements come out as intended:

$ ls test bar_002.tst baz_003.tst faz_004.tst foo_001.tst $ cat test.list 001 002 003 004 $ perl -pale '$p=<test/*_$F[0].tst>; s/^/$p /' test.list test/foo_001.tst 001 002 test/baz_003.tst 003 004
I can't figure out why the glob only works on every other iteration, or how to make it work on every iteration. This perl 5.8.7, on both macosx and freebsd.

(The made-up example is simpler than the real case: the directory actually contains thousands of files, and I have a list of 200 ids to extract path/file strings for. I know there are other ways to do this, but I'm curious about the behavior of the file glob in this case.)

UPDATE: Nevermind -- I should have looked at perldoc -f glob first:

glob In list context, returns a (possibly empty) list of filename expansions on the value of EXPR such as the standard Unix she +ll /bin/csh would do. In scalar context, glob iterates through such filename expansions, returning undef when the list is exhausted.
Just changing the one-liner to start with '($p)=...' did the trick. Sorry for the spurious post.

Replies are listed 'Best First'.
Re: Problem with one-liner using file globs
by grinder (Bishop) on Mar 13, 2007 at 17:37 UTC

    You are having problems with cryptocontext. glob behaves differently according to whether you use it in scalar or list context.

    Your code as it stands calls glob in scalar context ($p = ...). This primes glob to set up an iterator that may return a different filename each time you call it, for filenames that match the pattern, until no more do, at which time it returns undef.

    The second time you call glob with '002', you're getting the undef back that signals that nothing else matches the glob set up for '001'. By that time, the glob has been exhausted, so when you call it the next time with '003', you get something back. And so forth.

    You can change your one-liner to get an array back, and just use the first element returned:

    perl -pale 'print $F[0]; @p=<test/*_$F[0].tst>; s/^/$p[0] /' test.list

    You may wish to iterate over @p in a loop, and deal with all that match (I cannot know if this is an issue for you or not).

    Otherwise, if you only care about ever dealing with one entry, you can force a list assignment with parentheses:

    perl -pale 'print $F[0]; ($p) = <test/*_$F[0].tst>; s/^/$p /' test.lis +t

    update: oh bleagh, while I was testing all this out, you found the right answer anyway. So shoot me. Oh well, I hope someone finds this instructive.

    • another intruder with the mooring in the heart of the Perl