tags:

views:

85

answers:

3

This is a really basic issue, but I'm new to perl and cannot work out what the issue is. I'm just trying to isolate the files in a directory, but the -d operator keeps treating all the folder contents as files ...

@contents is my array, and when I run this:

use strict;

if ($ARGV[1]) {
    die("Error: You can only monitor one directory at a time\n");
}

my $directory = $ARGV[0] || die "Error: No directory defined\n";

opendir(DIR, $directory) || die "Error: Can't open dir $directory: $!";
my @contents = readdir(DIR);

foreach my $item(@contents) {
    if (-d $item) { next; }
    print"$item is a file\n"; 
}

closedir (DIR);

I keep getting both folders and files. Alternatively, if I use -f, I get nothing.

edit: this is the output -

file01.txt is a file
folder 01  is a file
folder 02 is a file
Screen shot 2010-04-18 at 1.26.17 PM.png is a file

I'm running this on OSX

edit:dir ls -l output:

aaron ~/Documents/test: ls -l
total 112
-rw-r--r--@ 1 aaron  staff  51235 18 Apr 13:26 Screen shot 2010-04-18 at 1.26.17 PM.png
-rw-r--r--@ 1 aaron  staff      7 18 Apr 13:26 file01.txt
drwxr-xr-x  3 aaron  staff    102 18 Apr 13:25 folder 01
drwxr-xr-x  2 aaron  staff     68 18 Apr 13:25 folder 02
+2  A: 

I guess your @contents has newlines at the end of the file. Try adding chomp:

foreach $item(@contents) {
  chomp($item);
  next if (-d $item); 
  print"$item is a file\n";       
}
codaddict
Could be a good guess - as so often, the key information isn't in the information given in the question. The trailing blank on '`folder 01 `' is interesting, too.
Jonathan Leffler
@Jonathan: Reason for guessing is if he uses a `glob` to populate `@contents` as you've done there will not be trailing newlines, but if he uses backticks: `@contents=`ls`;` there will be trailing newlines.
codaddict
+4  A: 

Solution

I was testing with '.' as the directory...you're testing with some other directory. The names read from the directory are then checked relative to the current directory. If I use some other directory name, I'll get almost everything except '.' and '..' listed as files, regardless.

If you prefix the name with the value of $ARGV[0], you'll get the expected result:

#!/bin/perl -w

use strict;

if ($ARGV[1]) {
    die("Error: You can only monitor one directory at a time\n");
}

my $directory = $ARGV[0] || die "Error: No directory defined\n";

opendir(DIR, $directory) || die "Error: Can't open dir $directory: $!";
my @contents = readdir(DIR);

foreach my $item(@contents) {
    next if -d "$ARGV[0]/$item";
    print "$ARGV[0]/$item is a file\n"; 
}

closedir (DIR);

Previous attempts to explain

This works on MacOS X:

#!/bin/perl -w
use strict;

my @contents = <*>;

foreach my $item (@contents)
{
    print "== $item\n";
    next if -d $item;
    print "$item is a file\n";
}

Test:

MiniMac JL: perl -c xx.pl
xx.pl syntax OK
MiniMac JL: perl xx.pl
== cproto-4.7g
== fpqsort1
fpqsort1 is a file
== fpqsort1.h
fpqsort1.h is a file
== fpqsort2
fpqsort2 is a file
== fpqsort2.c
fpqsort2.c is a file
== gcc-predef.h
gcc-predef.h is a file
== git-1.6.5.7
== go
== makefile
makefile is a file
== qs-test1.c
qs-test1.c is a file
== qs-test2.c
qs-test2.c is a file
== RCS
== rep-report.txt
rep-report.txt is a file
== select.c
select.c is a file
== soq
== xx.pl
xx.pl is a file
MiniMac JL: 

Given a marginally modified version of the code in the question:

#!/bin/perl -w

use strict;

if ($ARGV[1]) {
    die("Error: You can only monitor one directory at a time\n");
}

my $directory = $ARGV[0] || die "Error: No directory defined\n";

opendir(DIR, $directory) || die "Error: Can't open dir $directory: $!";
my @contents = readdir(DIR);

foreach my $item(@contents) {
    print "<<$item>>\n";
    next if -d $item;
    print"$item is a file\n"; 
}

closedir (DIR);

Running it on the same directory as before, I get the output:

Minimac JL: perl yy.pl .
<<.>>
<<..>>
<<cproto-4.7g>>
<<fpqsort1>>
fpqsort1 is a file
<<fpqsort1.h>>
fpqsort1.h is a file
<<fpqsort2>>
fpqsort2 is a file
<<fpqsort2.c>>
fpqsort2.c is a file
<<gcc-predef.h>>
gcc-predef.h is a file
<<git-1.6.5.7>>
<<go>>
<<makefile>>
makefile is a file
<<qs-test1.c>>
qs-test1.c is a file
<<qs-test2.c>>
qs-test2.c is a file
<<RCS>>
<<rep-report.txt>>
rep-report.txt is a file
<<select.c>>
select.c is a file
<<soq>>
<<xx.pl>>
xx.pl is a file
<<yy.pl>>
yy.pl is a file
Minimac JL:

Note the Perlish idiom 'next if -d $item;'. Also note the debugging techniques: print the names as they go through the array - using the '<<' and '>>' to surround the name helps identify odd side effects (such as newlines in names). I did double check that the provided code produces the same result - it does. And I'm running on a MacOS X 10.6.3 with the stock Perl.

Jonathan Leffler
Hi Jonathan, I'm passing the dir in as a arg. Would this have anything to do with it? I've placed the complete code above.
Aaron Moodie
@Jonathan: Good catch..even I was testing with `.` :)
codaddict
@Jonathan! THANKS! damn, what a waste of 4 hours ...
Aaron Moodie
one more question, why does `"$ARGV[0]/$item"` need to be in quotes?
Aaron Moodie
@Aaron: Because otherwise you're dividing $ARGV[0] by $item, and since both are likely to evaluate to 0, the system is unlikely to be happy with you.
Jonathan Leffler
@Jonathan ah, of course... Thanks :)
Aaron Moodie
+2  A: 

From the Perldoc on readdir:

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.

Debugged code:

#!/usr/bin/perl
use strict;

if ($ARGV[1]) {
    die("Error: You can only monitor one directory at a time\n");
}

my $directory = $ARGV[0] || die "Error: No directory defined\n";

opendir(DIR, $directory) || die "Error: Can't open dir $directory: $!";
my @contents = readdir(DIR);

foreach my $item (@contents) {
    print "\$item=$item\n";
    print "$item is a file \n" if (-f $directory."/".$item); 
    print"$item is a dir\n" if (-d $directory."/".$item);  
}

closedir (DIR);
drewk
Good quote from the perldoc page. Isn't it a bit neater to use `"$directory/$item"` rather than `$directory."/".$item`? Or is that just die-hard shell programmer thinking?
Jonathan Leffler
I thought it illustrated the prepending a bit better. I have a weakness for the `.` operator. If >>I<< were writing this, I would probably use `map` in the readdir step.
drewk