use of stat and argument isn't numeric message

Y

yapd

I saw a particular use of stat in a book. The snippet was:

if (!$stat[4] && !$stat[5] && !$stat[6] && !$stat[7] && !$stat[8]){
    return 0;
}

It would return from a subroutine that was determining if a file was
readable (else it was deemed damaged). I was wondering what, if anything
this particular snippet was checking. I would be grateful if someone
would reveal what I may be missing.

The entire program as it appeared in the text, for purposes of context,
is below.

A second issue I was hoping someone could comment on:
this error occuring on the bounds checking in the for loop:

"# read the file one byte at a time""

Argument "\x{4a}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{59}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{75}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6e}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{71}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6f}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{62}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6e}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6f}" isn't numeric in numeric lt (<) at line 61.

I get the message when perl is run with '-w'. However, it is quiet
otherwise.

Thanks for your comments!



###############

#*
#* search a filesystem "by hand" for damaged files
#*

use Cwd; # module for finding the current working directory
$|=1;    # turn off I/O buffering

sub ScanDirectory {
    my ($workdir) = shift;
    my($startdir) = &cwd; # keep track of where we began

    chdir($workdir) or die "Unable to enter dir $workdir:$!\n";
    opendir(DIR, ".") or die "Unable to open $workdir:$!\n";
    my @names = readdir(DIR);
    closedir(DIR);

    foreach my $name (@names){
        next if ($name eq ".");
        next if ($name eq "..");
        if (-d $name){ # is this a directory?
            &ScanDirectory($name);
            next;
        }
        unless (&CheckFile($name)){
            print &cwd."/".$name."\n"; # print the bad filename
        }
    }

    chdir($startdir) or die "Unable to change to dir $startdir:$!\n";
}

sub CheckFile{
    my($name) = shift;

    print STDERR "Scanning ". &cwd."/".$name."\n";

    # attempt to read the directory entry for this file
    my @stat = stat($name);
    if (!$stat[4] && !$stat[5] && !$stat[6] && !$stat[7] && !$stat[8]){
        return 0;
    }

    # attempt to open this file
    unless (open(T,"$name")){
        return 0;
    }

    # read the file one byte at a time
    for (my $i=0;$i< $stat[7];$i++){
        my $r=sysread(T,$i,1);
        if ($r !=1) {
            close(T);
            return 0;
        }
    }
    close(T);
    return 1;
}

&ScanDirectory(".");



###############
from chapter 2 of ORA's
Perl for System Administration
 
B

Ben Morrow

yapd said:
A second issue I was hoping someone could comment on:
this error occuring on the bounds checking in the for loop:

"# read the file one byte at a time""

Argument "\x{4a}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{59}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{75}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6e}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{71}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6f}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{62}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6e}" isn't numeric in numeric lt (<) at line 61.
Argument "\x{6f}" isn't numeric in numeric lt (<) at line 61.

I get the message when perl is run with '-w'. However, it is quiet
otherwise.
#*
#* search a filesystem "by hand" for damaged files
#*

use Cwd; # module for finding the current working directory
$|=1;    # turn off I/O buffering

sub ScanDirectory {
    my ($workdir) = shift;
    my($startdir) = &cwd; # keep track of where we began

Don't call subs with &. This is perl4ish. Read perldoc perlsub to find
out what effects it has.
    chdir($workdir) or die "Unable to enter dir $workdir:$!\n";

Hmmm... I wouldn't put "\n" on the end of that. It suppresses useful
information.
    opendir(DIR, ".") or die "Unable to open $workdir:$!\n";
    my @names = readdir(DIR);
    closedir(DIR);

    foreach my $name (@names){
        next if ($name eq ".");
        next if ($name eq "..");
        if (-d $name){ # is this a directory?
            &ScanDirectory($name);
            next;
        }
        unless (&CheckFile($name)){
            print &cwd."/".$name."\n"; # print the bad filename
        }
    }

    chdir($startdir) or die "Unable to change to dir $startdir:$!\n";
}

sub CheckFile{
    my($name) = shift;

    print STDERR "Scanning ". &cwd."/".$name."\n";

    # attempt to read the directory entry for this file
    my @stat = stat($name);
    if (!$stat[4] && !$stat[5] && !$stat[6] && !$stat[7] && !$stat[8]){

Well, this appears to check for a file user and group root, not a
special file, zero-length and not accessed since the epoch. I have to
say I can't see any reason for singling out this particular class of
file to consider 'bad', but then I'm not an expert on Unix system
administration.
        return 0;
    }

    # attempt to open this file
    unless (open(T,"$name")){

I would always use a lexical filehandle, and there's no need to quote
$name:

unless (open my $T, $name) {
        return 0;

You should simply 'return' rather than 'return 0' when you want to
return a false value: this returns undef in scalar context and the
empty list in list context, so it's always false.
    }

    # read the file one byte at a time
    for (my $i=0;$i< $stat[7];$i++){

Bleech.

for (1..$stat[7]) {

You don't actually use the loop counter, so it doesn't matter that
I've both renamed it (to $_) and changed the values by one.
        my $r=sysread(T,$i,1);

This is a bug. $i is both the loop counter and the variable you are
reading into. You want

my $r = sysread $T, my $tmp, 1;
        if ($r !=1) {
            close(T);
            return 0;
        }

If you'd've used a lexical FH above, it would close automatically on
scope exit.

I think I would have written

undef $!;
local $/ = \1;
$! and return while <$T>;
$. == $stat[7] or return;

although that does something slightly different (the reading is done
through a buffer). If I wanted to use sysread, I would probably have
done

1 == sysread $T, my $tmp, 1 or return for 1..$stat[7];

but that is arguably less clear than unwrapping the loop.
    close(T);
    return 1;
}

&ScanDirectory(".");
###############
from chapter 2 of ORA's
Perl for System Administration

If this is the general standard of the scripts in that book, throw it
away and get another one.

Ben
 
M

MPBroida

Ben said:
# read the file one byte at a time
for (my $i=0;$i< $stat[7];$i++){

Bleech.

for (1..$stat[7]) {

But will that construct react to changes in $stat[7]
within the loop?? What I mean is: will perl look at
$stat[7] only on the FIRST time through the loop and
save that value as the end of the range? Or will it
re-evaluate $stat[7] on each pass through the loop??

With the "expanded" loop format the OP used, I'm sure
(well, pretty sure) it will re-evaluate $stat[7] on
each pass. Is it guaranteed that Perl will re-eval
the end value with the ".." range format?

Mike
 
G

gnari

MPBroida said:
Ben said:
# read the file one byte at a time
for (my $i=0;$i< $stat[7];$i++){

Bleech.

for (1..$stat[7]) {

But will that construct react to changes in $stat[7]
within the loop?? What I mean is: will perl look at
$stat[7] only on the FIRST time through the loop and
save that value as the end of the range?
yes

Or will it
re-evaluate $stat[7] on each pass through the loop??
no


With the "expanded" loop format the OP used, I'm sure
(well, pretty sure) it will re-evaluate $stat[7] on
each pass.

yes

but in this case re-evaluation was not needed


gnari
 
M

MPBroida

gnari said:
MPBroida said:
Ben said:
# read the file one byte at a time
for (my $i=0;$i< $stat[7];$i++){

Bleech.

for (1..$stat[7]) {

But will that construct react to changes in $stat[7]
within the loop?? What I mean is: will perl look at
$stat[7] only on the FIRST time through the loop and
save that value as the end of the range?
yes

Or will it
re-evaluate $stat[7] on each pass through the loop??
no


With the "expanded" loop format the OP used, I'm sure
(well, pretty sure) it will re-evaluate $stat[7] on
each pass.

yes

but in this case re-evaluation was not needed

OK, so in cases where the end of the range may vary within
the loop, it would be BAD to use the "start..end" kind of
if loop. I'll try to remember that. :) In other cases,
it is definitely much cleaner/simpler to use "..".

Thanks!
Mike
 
G

gnari

OK, so in cases where the end of the range may vary within
the loop, it would be BAD to use the "start..end" kind of
if loop. I'll try to remember that. :) In other cases,
it is definitely much cleaner/simpler to use "..".

just remember that .. is a list constructor
for my $i (1..100000) {...}
iterates through a (long) list of elements
the for (;;) loop is just a funny kind of while loop

gnari
 
A

Anno Siegel

gnari said:
just remember that .. is a list constructor
for my $i (1..100000) {...}
iterates through a (long) list of elements

But the list isn't expanded. The compiler is clever enough to generate
the elements one by one at run time.

This hasn't always been so, but the feature was introduced early in
Perl 5.x.

Anno
 
M

MPBroida

Anno said:
But the list isn't expanded. The compiler is clever enough to generate
the elements one by one at run time.

This hasn't always been so, but the feature was introduced early in
Perl 5.x.

Sounds like you disagree with gnari (unless I'm missing something).
Alright, at the bell, come out fighting. Need an authoritative
answer:

Will:
for (1..$a)
recalculate the end of the range on EACH pass???
This is important if $a changes inside the loop.

Mike
 
G

gnari

MPBroida said:
Sounds like you disagree with gnari (unless I'm missing something).

no, not at all. he was expanding on what i said. I never said the list
was expanded. I just sait it was a list (or behaved like one)
Alright, at the bell, come out fighting. Need an authoritative
answer:

Will:
for (1..$a)
recalculate the end of the range on EACH pass???
This is important if $a changes inside the loop.
no. this is easily tested:
perl -le "my $a=5;for (1..$a) {print $_,$a++}"
15
26
37
48
59

gnari
 
E

Eric Schwartz

MPBroida said:
Will:
for (1..$a)
recalculate the end of the range on EACH pass???

I don't suppose you tried it yourself to see?

$a=4;
for(1..$a) {
print "$a\n";
$a++
}
__END__
4
5
6
7

This pretty clearly indicates that Perl does not recalculate the end
value if $a changes in the middle of the loop; if it did, the loop
would never terminate.

-=Eric
 
B

Ben Morrow

MPBroida said:
Sounds like you disagree with gnari (unless I'm missing something).

Yup, you are. What Anno is saying is that

for my $i (1..100_000) {...}

doesn't actually create a list of 100_000 elements in memory, it just
makes a note that there are 100_000 elements to iterate over. If you
were to write

my @a = 1..100_000;
for my $i (@a) {...}

, OTOH, the list would actually be constructed and put into the array
before the for loop was started.

For a generalization of this concept, see Perl6's lazy lists.

Ben
 
M

MPBroida

Ben said:
Yup, you are. What Anno is saying is that

for my $i (1..100_000) {...}

doesn't actually create a list of 100_000 elements in memory, it just
makes a note that there are 100_000 elements to iterate over. If you
were to write

Ok, that is what I was missing: the fact that it (in a way)
creates a COUNT of how many times it will loop. And it must
also remember the "start" (or "current") value, of course. :)

Mike
 
M

MPBroida

Eric said:
I don't suppose you tried it yourself to see?

$a=4;
for(1..$a) {
print "$a\n";
$a++
}
__END__
4
5
6
7

This pretty clearly indicates that Perl does not recalculate the end
value if $a changes in the middle of the loop; if it did, the loop
would never terminate.

Good example. Thanks!

Mike
 
M

MPBroida

gnari said:
no, not at all. he was expanding on what i said. I never said the list
was expanded. I just sait it was a list (or behaved like one)

no. this is easily tested:
perl -le "my $a=5;for (1..$a) {print $_,$a++}"
15
26
37
48
59

OK, now I see what is happening. :)
Thanks to all for clarifying this stuff for me.
I'll make sure I do NOT use 1..$a and expect it to
adjust to changes in $a. :)

Thanks!
Mike
 

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,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top