Question

I have a perl script which reads a directory and prints out the files for that directory among other things, however I am unable to do so as I have no read permissions..

sub func {
    my $some_dir = $_[0];
    opendir(my $DIR, $some_dir) or die "ERROR!";
    while (my $file = readdir($DIR)) {
        if (-r $file) {              # Read permissions
            print "hurray $file!\n";
            if (-d $file) {          # Directory 
               print "entering dir...\n";
               func($file);
            }
        } else {                     # No read permissions..
            print "no permissions for $file\n";
        }
    }
    return;
}

If I run this code with a folder structure:

file
folder
-file
-file
-folder
--file
--file

I get:

hurray file!
hurray folder!
entering dir..
no permissions for file
no permissions for file
no permissions for file # no idea why?? should be folder
no permissions for file
no permissions for file

So it only prints the files/folders that are in the starting directory, everything else has no read permissions. I tried then doing chmod 777 file to all of the files in the sub-directories, but I get the same result..

Any ideas?

Thanks.

EDIT - Added some things to clear up confusion.

Was it helpful?

Solution

Here is the documentation for readdir (obtained by perldoc -f readdir):

Returns the next directory entry for a directory opened by "opendir". If used in list context, returns all the rest of the entries in the directory. If there are no more entries, returns an undefined value in scalar context or a null list in list context.

If you're planning to filetest the return values out of a "readdir", you'd better prepend the directory in question. Otherwise, because we didn't "chdir" there, it would have been testing the wrong file.

opendir(my $dh, $some_dir) || die "can't opendir $some_dir: $!";
@dots = grep { /^\./ && -f "$some_dir/$_" } readdir($dh);
closedir $dh;

Thus:

  1. your if (-r $file) will not work, you will have to say if (-r "$some_dir/$file").

  2. readdir will also return the directories '.' and '..' and you probably are not interested in those.

OTHER TIPS

The problem is that your working directory does not change. For example, assuming your starting directory is /home:

opendir(my $DIR, $some_dir) or die "ERROR!";    # working dir is /home
while (my $file = readdir($DIR)) {
    if (-r $file) {                             # checks for /home/$file
        print "hurray $file!\n";                # but path is /home/folder/$file
        if (-d $file) {
           print "entering dir...\n";           
           func($file);                         # enters /home/folder/$file

So from /home, the script cannot see a file folder/file, or, at least not the one you mean. I discovered that I had some similarly named files in my test directory and my scripts working directory which gave false positives.

The solution is to either chdir as appropriate, or to add the path to the file name when checking permissions. You should also compensate for the . (current dir) and .. (parent dir). When I ran your script, as-is, it turned into an infinite loop.

But of course, the best solution is to use a proper module for the job: File::Find:

use strict;
use warnings;
use File::Find;

my $file = shift;   # your file name
find(sub { if (-r) { print "Hurray $File::Find::name\n"; } }, $file);

This will recursively go through a folder and print out names of files that match the -r permission. If you read the documentation for File::Find you will find that the find subroutine automatically changes directory, and uses the variables:

$_                    # file name e.g. 'file.txt'
$File::Find::name     # full relative path e.g. 'folder/file.txt'
$File::Find::dir      # current folder name e.g. 'folder'
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top