1
votes

In Perl, When I'm trying to read dir in a loop, and perform for each file stat() in order to get $size and $mode, I get wrong data!

For example, I just created simple text file and it shows me it has permission 0000 and no size.

Perl Code:

if (!@ARGV[0]) {
    die("Za mało parametrów!\n");
}

$dirname = @ARGV[0];

opendir(DIR, $dirname) || die("Nie mogę otworzyć katalogu!\n");
while( $filename = readdir(DIR) ) {

    ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
         $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);

    $perm = sprintf("%04o", $mode & 07777);
    $tmp1 = int(($size/1024));
    $tmp2 = length($filename);

    if (($tmp1 > $tmp2) && ($perm =~ /.[^5410]../)) {
        print("$filename\n");
    }
}
closedir(DIR);
2
You should always enable warnings when developing Perl code (then change @ARGV[0] to $ARGV[0]). The solution to your problem is given in the second paragraph of the docs for the readdir() function.tadmc

2 Answers

7
votes

You will need to pass the full filepath to the stat() function. At the moment you are just passing a filename, so the script will look in its current directory for this file.

In other words, do this:

($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
         $atime,$mtime,$ctime,$blksize,$blocks) = stat("$dirname/$filename");
0
votes

As mentioned by others, the problem is that you are trying to operate on files in $dirname with only the file name (as per the readdir documentation). Without the full path, stat cannot find the file.

One could concatenate the directory to each file name and perhaps even make the result absolute (see my comment to the other answer), but this is a pain in the neck.

The other way to operate on files in $dirname is to change the working directory to the directory in question, operate and then return to the original. My favorite way to cd is the File::chdir module, which creates the scalar $CWD which is tied to the current working directory. This does exactly what I described when it is made local to a block and changed to your directory in question. Then you can do something like:

use strict;
use warnings;

use File::chdir;

$dirname = shift @ARGV or die("Za mało parametrów!\n") ;

{
    local $CWD = $dirname; #changes the cwd to contents of $dirname

    opendir(my $dir, $CWD) || die("Nie mogę otworzyć katalogu!\n");
    while( $filename = readdir($dir) ) {

        ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
             $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);

        $perm = sprintf("%04o", $mode & 07777);
        $tmp1 = int(($size/1024));
        $tmp2 = length($filename);

        if (($tmp1 > $tmp2) && ($perm =~ /.[^5410]../)) {
            print("$filename\n");
        }
    }

}

After the block, the original cwd is restored. Note: I have not tested this code for this case. I use this method often. It should fix the problem, and it's portable!