Problem expanding filenames in loop

H

Hemant

Folks,

When I try to expand file name in a loop, perl fails to expand
everyother file.

Example:

#!/usr/bin/perl
#

@FileListArr = (
"Full-2008-06-16-23:29:40",
"Incr-2008-06-17-22:02:18",
"Incr-2008-06-18-22:02:23",
"Incr-2008-06-19-22:02:20",
"Incr-2008-06-20-22:02:38",
"Incr-2008-06-21-22:05:30",
"Incr-2008-06-22-22:05:26",
);

foreach $Volume (@FileListArr)
{
$File=glob("/backups/backupfiles/*/$Volume");
if (!defined $File)
{
print "File not found for $Volume\n";
}
else
{
print "$File\n";
}
}


The output of the script:

/backups/backupfiles/lidp11/Full-2008-06-16-23:29:40
File not found for Incr-2008-06-17-22:02:18
/backups/backupfiles/lidp11/Incr-2008-06-18-22:02:23
File not found for Incr-2008-06-19-22:02:20
/backups/backupfiles/lidp11/Incr-2008-06-20-22:02:38
File not found for Incr-2008-06-21-22:05:30
/backups/backupfiles/lidp11/Incr-2008-06-22-22:05:26


I also tried using "<>" keyword, but got same result. I tried similar
test to expand user's HOME directory (tilde expansion) and for same
result.

I tested it under perl 5.8.4 on AIX and perl 5.10.0 on linux.
 
J

John W. Krahn

Hemant said:
Folks,

When I try to expand file name in a loop, perl fails to expand
everyother file.

Example:

#!/usr/bin/perl
#

@FileListArr = (
"Full-2008-06-16-23:29:40",
"Incr-2008-06-17-22:02:18",
"Incr-2008-06-18-22:02:23",
"Incr-2008-06-19-22:02:20",
"Incr-2008-06-20-22:02:38",
"Incr-2008-06-21-22:05:30",
"Incr-2008-06-22-22:05:26",
);

foreach $Volume (@FileListArr)
{
$File=glob("/backups/backupfiles/*/$Volume");
if (!defined $File)
{
print "File not found for $Volume\n";
}
else
{
print "$File\n";
}
}


The output of the script:

/backups/backupfiles/lidp11/Full-2008-06-16-23:29:40
File not found for Incr-2008-06-17-22:02:18
/backups/backupfiles/lidp11/Incr-2008-06-18-22:02:23
File not found for Incr-2008-06-19-22:02:20
/backups/backupfiles/lidp11/Incr-2008-06-20-22:02:38
File not found for Incr-2008-06-21-22:05:30
/backups/backupfiles/lidp11/Incr-2008-06-22-22:05:26


I also tried using "<>" keyword, but got same result. I tried similar
test to expand user's HOME directory (tilde expansion) and for same
result.

I tested it under perl 5.8.4 on AIX and perl 5.10.0 on linux.

From the perlop.pod man page:

perldoc perlop
[snip]
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 it's probably better to use something like this:

foreach my $File ( map glob("/backups/backupfiles/*/$_"), @FileListArr )
{
print "$File\n";
}



John
 
X

xhoster

Hemant said:
Folks,

When I try to expand file name in a loop, perl fails to expand
everyother file.

Example:
....

foreach $Volume (@FileListArr)
{
$File=glob("/backups/backupfiles/*/$Volume");

call it in a list context:

($File)=glob("/backups/backupfiles/*/$Volume");

In a scalar context, glob iterates through the expansions, returning
undef when done. The fact that $Volume has changed in the mean time
doesn't matter. Glob doesn't look for such changes until after it is done
iterating on the expansion is is currently working over. In your case,
if the expansion always expands to exactly one file, then you will
get this every other effect.

1) Notice the argument. Return the file.
2) Fail to notice the argument change. Return undef, to indicate no more
files for the previous argument.
3) Since its starting from empty, Notice the argument again. Return that
file.

etc.

From perldoc perlop:

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.

In my experience, it is almost always a mistake to use glob in a scalar
context.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
B

Ben Morrow

Quoth Hemant said:
When I try to expand file name in a loop, perl fails to expand
everyother file.

Example:

#!/usr/bin/perl
#

use warnings;
use strict;
@FileListArr = (

my @FileListArr = (
"Full-2008-06-16-23:29:40",
"Incr-2008-06-17-22:02:18",
"Incr-2008-06-18-22:02:23",
"Incr-2008-06-19-22:02:20",
"Incr-2008-06-20-22:02:38",
"Incr-2008-06-21-22:05:30",
"Incr-2008-06-22-22:05:26",
);

It is often more convenient to write a list like this as

my @FileListArr = qw(
Full-2008-06-16-23:29:40
Incr-2008-06-17-22:02:18
);
foreach $Volume (@FileListArr)

foreach my $Volume (@FileListArr)
{
$File=glob("/backups/backupfiles/*/$Volume");

glob in scalar context doesn't do what you think. Once you've invoked it
once, it ignores its argument until it's returned all the possible
results and then undef. If you just want the first result, you need to
call it in list context like this:

my ($File) = glob(...);

where the parens around ($File) make this a list assignment (into a list
with only one value) rather than a scalar assignment.

Ben
 
H

Hemant

Quoth Hemant <[email protected]>:




use warnings;
use strict;


my @FileListArr = (


It is often more convenient to write a list like this as

my @FileListArr = qw(
Full-2008-06-16-23:29:40
Incr-2008-06-17-22:02:18
);


foreach my $Volume (@FileListArr)


glob in scalar context doesn't do what you think. Once you've invoked it
once, it ignores its argument until it's returned all the possible
results and then undef. If you just want the first result, you need to
call it in list context like this:

my ($File) = glob(...);

where the parens around ($File) make this a list assignment (into a list
with only one value) rather than a scalar assignment.

Ben

Thank you all for the quick replies.
 
E

Eric Pozharski

*SKIP*
glob in scalar context doesn't do what you think. Once you've invoked it
once, it ignores its argument until it's returned all the possible
results and then undef. If you just want the first result, you need to
call it in list context like this:
my ($File) = glob(...);
where the parens around ($File) make this a list assignment (into a list
with only one value) rather than a scalar assignment.

As of Perl 5.8.8 (for Debian Etch) it makes no difference:

09:57:37 14 [0:9]$ perl -e '($x) = glob q(/*); print $x, "\n"; print
glob(q(/var/*)), "\n"'
/bin
/var/backups/var/cache/var/games/var/lib/var/local/var/lock/var/log
/var/lost+found/var/mail/var/opt/var/quota.group/var/quota.user/var/run
/var/spool/var/state/var/tmp/var/www
09:57:49 15 [0:0]$ perl -e '$x = glob q(/*); print $x, "\n"; print
glob(q(/var/*)), "\n"'
/bin
/var/backups/var/cache/var/games/var/lib/var/local/var/lock/var/log
/var/lost+found/var/mail/var/opt/var/quota.group/var/quota.user/var/run
/var/spool/var/state/var/tmp/var/www
09:57:58 16 [0:0]$ perl -e '$x = glob q(/*); print $x, "\n"; print
glob(), "\n"; print glob(q(/var/*)), "\n"'
/bin

/var/backups/var/cache/var/games/var/lib/var/local/var/lock/var/log
/var/lost+found/var/mail/var/opt/var/quota.group/var/quota.user/var/run
/var/spool/var/state/var/tmp/var/www
09:59:27 17 [0:0]$ perl -V
Summary of my perl5 (revision 5 version 8 subversion 8) configuration:

[Stealing thread]

(Sorry for somewhat messy screenshots) However, I have (had?) a problem
with B<forcing> list context for C<glob> in order to B<count>
occurences. I've tried:

10:15:35 23 [0:0]$ perl -e 'print glob q(/*); print "\n"'
/bin/boot/cdrom/cdrom0/dev/etc/floppy/home/initrd.img/initrd.img.old
/lib/lost+found/media/mnt/opt/proc/root/sbin/sys/tmp/usr/var/vmlinuz
/vmlinuz.old
10:15:44 24 [0:0]$ perl -e 'print (glob q(/*)); print "\n"'
/bin/boot/cdrom/cdrom0/dev/etc/floppy/home/initrd.img/initrd.img.old
/lib/lost+found/media/mnt/opt/proc/root/sbin/sys/tmp/usr/var/vmlinuz
/vmlinuz.old
10:16:12 25 [0:0]$ perl -e 'print scalar(glob q(/*)); print "\n"'
/bin
10:16:21 26 [0:0]$ perl -e 'print scalar((glob q(/*))); print "\n"'
/bin

And ended with:

10:16:30 27 [0:0]$ perl -e 'print scalar(@{[ glob q(/*) ]}); print "\n"'
24

That looks somewhat awful, doesn't it? OK, after all I want to count.
That doesn't work (I don't understand DWIM?) too:

10:21:32 30 [0:0]$ perl -e 'print glob q(/*) > 1; print "\n"'

10:23:00 31 [0:0]$ perl -e 'print (glob q(/*)) > 1; print "\n"'
/bin/boot/cdrom/cdrom0/dev/etc/floppy/home/initrd.img/initrd.img.old
/lib/lost+found/media/mnt/opt/proc/root/sbin/sys/tmp/usr/var/vmlinuz
/vmlinuz.old
10:23:20 32 [0:0]$ perl -e 'print (glob q(/*)) > 1; print "\n"'
10:23:26 32 [0:1]$ perl -e 'print scalar(glob q(/*)) > 1; print "\n"'

10:23:58 33 [0:0]$ perl -e 'print scalar((glob q(/*))) > 1; print "\n"'

10:24:07 34 [0:0]$ perl -e 'print scalar(@{[ glob q(/*) ]}) > 1; print
"\n"'
1
 
E

Eric Pozharski

Eric Pozharski said:
10:23:00 31 [0:0]$ perl -e 'print (glob q(/*)) > 1; print "\n"'

/bin/boot/cdrom/cdrom0/dev/etc/floppy/home/initrd.img/initrd.img.old
/lib/lost+found/media/mnt/opt/proc/root/sbin/sys/tmp/usr/var/vmlinuz
/vmlinuz.old

*CUT*
 
X

xhoster

Eric Pozharski said:
*SKIP*




As of Perl 5.8.8 (for Debian Etch) it makes no difference:

It isn't clear to me what difference you think it should make.
09:57:37 14 [0:9]$ perl -e '($x) = glob q(/*); print $x, "\n"; print
glob(q(/var/*)), "\n"'

The above has no loops. Neither of the globs in the above is encountered
twice. Each occurrence of glob-in-scalar-context maintains its own
iterator state. To have one global iterator for all globs in the program
would be bizarre. (Even more bizarre)

....
[Stealing thread]

(Sorry for somewhat messy screenshots) However, I have (had?) a problem
with B<forcing> list context for C<glob> in order to B<count>
occurences. I've tried: ...
And ended with:

10:16:30 27 [0:0]$ perl -e 'print scalar(@{[ glob q(/*) ]}); print "\n"'
24

That looks somewhat awful, doesn't it?

perl -e 'print scalar(() = glob q(/*)); print "\n"'


Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
B

Ben Morrow

Quoth Eric Pozharski said:
*SKIP*




As of Perl 5.8.8 (for Debian Etch) it makes no difference:

You're testing the wrong thing... which is not surprising, since where
Perl keeps its implicit iterators is never very clear. Scalar glob keeps
its iterator *in the op*: that is, each instance of 'glob' in the source
is iterating independantly. So you have to call the same one twice to
see any odd effects:

~% perl -le'print glob("/bin") for 1..2'
/bin
/bin
~% perl -le'print scalar glob("/bin") for 1..2'
/bin

~%

or indeed

~% perl -le'print glob("/$_") for qw/bin usr/'
/bin
/usr
~% perl -le'print scalar glob("/$_") for qw/bin usr/'
/bin

~%
09:57:37 14 [0:9]$ perl -e '($x) = glob q(/*); print $x, "\n"; print
glob(q(/var/*)), "\n"'

(you would do well to learn about the -l switch... :) )

(Sorry for somewhat messy screenshots) However, I have (had?) a problem
with B<forcing> list context for C<glob> in order to B<count>
occurences. I've tried:

10:15:35 23 [0:0]$ perl -e 'print glob q(/*); print "\n"'

glob is in list context.
10:15:44 24 [0:0]$ perl -e 'print (glob q(/*)); print "\n"'

glob is still in list context: the extra parens around print's arguments
make no difference.
10:16:12 25 [0:0]$ perl -e 'print scalar(glob q(/*)); print "\n"'

glob is in scalar context, so it iterates.
10:16:21 26 [0:0]$ perl -e 'print scalar((glob q(/*))); print "\n"'

glob is still in scalar context: more layers of parens still don't make
any difference.
10:16:30 27 [0:0]$ perl -e 'print scalar(@{[ glob q(/*) ]}); print "\n"'

OK, that works; but as you say, it's ugly, and it constructs a
completely useless array.
10:21:32 30 [0:0]$ perl -e 'print glob q(/*) > 1; print "\n"'

glob is in scalar context and returns '/bin'. '/bin' is numerically
equal to 0, so it is not greater than 1, so '>' returns false.
10:23:00 31 [0:0]$ perl -e 'print (glob q(/*)) > 1; print "\n"'

glob is in list context; the return value of print (1, probably) is
compared to 1, and the result thrown away. If you'd used -w you'd have
got a warning about that.
10:23:26 32 [0:1]$ perl -e 'print scalar(glob q(/*)) > 1; print "\n"'

This is the same as 'glob() > 1'. The 'scalar' is superflous.
10:23:58 33 [0:0]$ perl -e 'print scalar((glob q(/*))) > 1; print "\n"'

Same here.
10:24:07 34 [0:0]$ perl -e 'print scalar(@{[ glob q(/*) ]}) > 1; print
"\n"'

This works correctly, but again is deeply ugly.

The idiom you are looking for is

my $x = () = glob(...);

or, if you need a 'scalar',

print scalar( () = glob(...) );

I have no idea where this is documented, but if you assign to the empty
list in scalar context it returns a count of the items in the assigned
list. This is weird and not at all the way any other list assignment
works, but surprisingly useful.

Ben
 
X

xhoster

Ben Morrow said:
or, if you need a 'scalar',

print scalar( () = glob(...) );

I have no idea where this is documented, but if you assign to the empty
list in scalar context it returns a count of the items in the assigned
list.

Not just an empty list.

perl -le 'print scalar(my ($x,$z) = glob q(/*));'
24
This is weird and not at all the way any other list assignment
works, but surprisingly useful.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
B

Ben Morrow

Quoth (e-mail address removed):
Not just an empty list.

perl -le 'print scalar(my ($x,$z) = glob q(/*));'
24

Huh. I had it in my head that that returned a count of the LHS, not the
RHS. Don't know where I got that from...

....so, in fact, exactly like every other list assignment :).

Thanks,
Ben
 
E

Eric Pozharski

Ben Morrow said:
Quoth Eric Pozharski <[email protected]>: *SKIP*
You're testing the wrong thing... which is not surprising, since where
Perl keeps its implicit iterators is never very clear.

That's the second time I see you suppose some magic behind interpreter.
I like it. I suppose too.
Scalar glob keeps its iterator *in the op*: that is, each instance of

[ I'm sorry about that thread. It was based on my sole misunderstanding
what are (differences among) lists, arrays, assignments in scalar and
list context; and incomplete process of familarizing myself with
precedence and associativity. Now I feel much beter. ]
*SKIP*
10:24:07 34 [0:0]$ perl -e 'print scalar(@{[ glob q(/*) ]}) > 1; print
"\n"'
This works correctly, but again is deeply ugly.
The idiom you are looking for is
my $x = () = glob(...);
or, if you need a 'scalar',
print scalar( () = glob(...) );

That's what I missed (yes, I admit missing I<-l> and B<I<-w>>). To put
it clear:

22:16:07 143 [0:0]$ perl -le '$x = (1,2,4); print $x'
4
22:16:14 144 [0:0]$ perl -le '($x) = (1,2,4); print $x'
1
22:16:18 145 [0:0]$ perl -le '$x = () = (1,2,4); print $x'
3

The 1st is a B<comma operator>, I know. The 3rd slowly moves down to
backbone where it will stay forever. What surprises me most is the 2nd.
A long time ago, when I've just started the addiction to Perl, I was
told (OK, I've read), that intentional way to do that is
C<($x, undef) = (1, 2, 3)>; either way (C<($x) = (1, 2, 3)>) will set
I<$x> to the number of items in list. It's not so. Any more?

I've just checked, replacing the list with explicit array merges 1st and
3rd cases. And has no influence on the 2nd. Obviously, I've missed
some reading. What?
I have no idea where this is documented, but if you assign to the empty
list in scalar context it returns a count of the items in the assigned
list. This is weird and not at all the way any other list assignment
works, but surprisingly useful.

L<perlop/"Assignment operators">. In the last paragraph it's mentioned;
no explicit example though.
 
C

comp.llang.perl.moderated

Quoth (e-mail address removed):




Huh. I had it in my head that that returned a count of the LHS, not the
RHS. Don't know where I got that from...


...so, in fact, exactly like every other list assignment :).

Even with the list, it almost looks like Perl actually creates some
temp AV just to later grab its size perhaps...?

perl -MO=Concise -le 'print scalar( ($x,$z)=(1,2,3))'
....
aassign[t1] sKS ->c
....
 
B

Ben Morrow

Quoth "comp.llang.perl.moderated said:
...so, in fact, exactly like every other list assignment :).

Even with the list, it almost looks like Perl actually creates some
temp AV just to later grab its size perhaps...?

perl -MO=Concise -le 'print scalar( ($x,$z)=(1,2,3))'
...
aassign[t1] sKS ->c

No. aassign is just the name of the list assignment op. There's a lot of
confusion in older bits of perl between 'array' and 'list': I guess the
two concepts weren't properly separated to start with. 'wantarray', for
instance, ought to be called 'wantlist'.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top