Viin
Viin

Reputation: 517

directory was detected as file in perl

I tried to use this condition (-d $var) in a loop to check how many child directories on the given directory but some folders were detected as a file instead. What happened to the condition that failed to recognized the directory? How can i resolve this problem?

My code is:

foreach my $file (@files) {
    next if($file =~ m/^\./);
    if (-d $file and -e $file and !$seen{$file}) {
        $seen{$file} = 1;
        push @Dir, "$pwd/$file";
    }
    next if ($file !~ m/\s/i);
    my $new_name = s/\s//g;
    `mv $pwd/$file $pwd/$new_name`;
}

Upvotes: 2

Views: 184

Answers (3)

Borodin
Borodin

Reputation: 126722

There is a number of problems with your code. Why do you think the -d test is failing? The same rename is applied to both directories and files; the only difference is that directories are additionally pushed onto the @dir array the first time they are seen.

I'm not clear exactly what you want to do, but this version renames only files that contain whitespace characters in their name.

foreach my $file (@files) {

    next if $file =~ m/^\./;

    if (-d $file ) {
        push @dir, "$pwd/$file" unless $seen{$file}++;
        next;
    }

    my $new_name = $file;
    if ($new_name =~ s/\s//g) {
        rename "$pwd/$file", "$pwd/$new_name" or warn $!;
    }
}

Upvotes: 1

David W.
David W.

Reputation: 107040

I don't see anything obvious. However, I did spot a bug.

  • You have my $new_name = s/\s//g;. You have = instead of =~.
  • You also don'/t say how you get $new_name in order to do the substitution.

Neither of these point to your issue itself.

The other possibility is that you use three different tests all anded together. I wonder if you are somehow running into a condition where -d passes, but the other conditions aren't true. You might want to separate them out.

I also notice you test whether the file is a directory with $file, but when you put the directory name into the @Dir array, you prepend it with $pwd. What's going on here? Do you need to do if ( -d "$pwd/$file" ) too?

I recommend that you put in some debug statements to see where you're having an issue.

Try this:

use strict;
use warnings;

use feature qw(say);
use File::Copy;

my %seen;
my @dir;
for my $file ( @files ) {
    say qq(DEBUG: Looking at "$file");
    next if $seen{$file};
    say qq(DEBUG: "$file" has not been previously seen);
    $seen{$file} = 1;
    next if $file =~ /^\./;
    say qq(DEBUG: "$file" does not start with a period);
    if ( -d $file ) {
       say qq(DEBUG: "$file" is a directory);
       push @dir, "$pwd/$file;
    }
    else { #DEBUG:
       say qq(DEBUG: "$file" is a file);
   } #DEBUG:
   my $new_name = $file;
   if ( $new_name =~ s/\s+//g ) {
      say qq(DEBUG: Moving file "$file" to "$new_name");
      move $file, $new_name or
        die qq(Couldn't move "$file" to "$new_name");
   }
}
  • The use feature qw(say); allows you to use the say command. This is just like print except it puts a new line on the end for you.
  • The use File::Copy module allows you to use the move statement. No more shelling out and being OS dependent. Perl comes with a whole flock of modules that make your life easier. For example, the File::Find allows you to find files and directories.
  • I include all files and directories in %seen (why not?) and check for that first before checking if it's a directory.
  • I use the move $file, $new_name or die qq(...) to see if that move statement worked. You should always test the output of your functions -- especially fail prone functions like moving a file name or copying it, etc.
  • Notice I do if ( $new_name =~ s/\s+//g ). This allows me to test whether $new_name has a space and remove those spaces all at the same time.
  • The qq(...) is like a double quote, but you can use quotation marks in the string without having to back quote them. This way, I can see if my file name has a space or a NL on the end of the name.

Once you have your code working, you can easily search for the string DEBUG: and remove the noise.

I haven't tested my code (It's hard when the program isn't really complete and I don't know what your data looks like), but I hope you get the idea. The DEBUG: statements will allow you to see more what is going on in your code and help you find your logic issue. About 1/2 the time the problem isn't in the program, but in the data.

Upvotes: 2

squiguy
squiguy

Reputation: 33370

This happened to me a while back writing a script similar to this. Try using the entire path with the File::Spec method catfile. I am assuming you are running this in a separate directory from the actual files and directories.

use Cwd;
use File::Spec;

my $dir = getcwd; # Or whatever directory name you want
my $full_name = File::Spec->catfile($dir, $file);
if (-d $full_name) {
  # Do something
} 

Upvotes: 1

Related Questions