H0tc@xe has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to loop through all files in a directory, read each file into a varible (@config) and then perform regex operations and spit out the output to a file. I'm having a problem reading the files into the variable. I'm getting "Can't open 'Filename.txt': No such file or directory at ./find.pl line 14", but there are files in the directory.
#!/usr/bin/perl -w # use warnings; use strict; opendir(DH, "Configs/"); my @files = readdir(DH); closedir(DH); foreach my $file (@files) { # Read config from .txt files in Dir. open my $fh, "<", $file or die "Can't open '$file': $!"; my @config = <$fh>; close $fh; chomp @config; my $configref = \@config; # Extract names from config. my @names = map { /^name \d+\.\d+\.\d+\.\d+ ([A-Za-z0- +9-_]+)$/ ? $1 : () } @{ $configref }; # Remove names from config. @config = grep { $_ !~ /^name / } @config; # Find unused name references. foreach my $name (@names) { if (!grep { $_ =~ /$name/ } @config) { print "name $name unused.\n"; } } # Extract objects from config. my @objects = map { /^(object|object-group) (network|s +ervice) ([A-Za-z0-9-_]+)$/ ? $3 : () } @{ $configref }; # Remove objects from config. @config = grep { $_ !~ /^(object|object-group) / } @co +nfig; # Find unused object references. foreach my $object (@objects) { if (!grep { $_ =~ /$object/ } @config) { print "object $object unused.\n"; } } }

Replies are listed 'Best First'.
Re: Trouble opening and reading file within loop (readdir)
by Anonymous Monk on Sep 21, 2015 at 22:23 UTC

    You've fallen for the basic readdir trap (you didn't RTFM close enough)

    one easy solution (read glob for caveats) my @files = glob "Configs/*";

    Path::Tiny is very convenient

    use Path::Tiny qw/ path /; for my $file ( path( "Configs/" )->children ){ my @config = $file->slurp_raw; ... }
      :) s/slurp_raw/lines_raw/
      I'm not understanding why readdir wont work in my case? The error is being thrown when the file in @files is being opened.

        I'm not understanding why readdir wont work in my case? The error is being thrown when the file in @files is being opened.

        Why not ? use my solutions?

        Have you read readdir?

        Its a very short read (only "79" words) and it explains exactly your case

        You're not alone in this, practically 99% of all questions about readdir are this same question

        Would you like to read readdir now, or simply start using something more convenient like glob, or better yet Path::Tiny?

Re: Trouble opening and reading file within loop
by GotToBTru (Prior) on Sep 22, 2015 at 13:05 UTC
    /howard$: ls test Apr15 May15 testpm.dat EDI204_blah testing_all_Oct14.tar testpm2.pl /howard$: perl -d -e 1 ... main::(-e:1): 1 DB<1> $path='/howard/test/' DB<2> opendir(DH,$path) DB<3> @files=readdir(DH) DB<4> closedir(DH) DB<5> x @files 0 '.' 1 '..' 2 'Apr15' 3 'EDI204_blah' 4 'May15' 5 'testing_all_Oct14.tar' 6 'testpm.dat' 7 'testpm2.pl' DB<6> open $fh,'<',$files[6] or die $! No such file or directory at (eval 22)[/home/edi/perl/5.8.8/lib/5.8.8/ +perl5db.pl:628] line 2. DB<7> open $fh,'<',$path . $file or die $! DB<8> close $fh DB<9> @fglob = glob($path . "*") DB<10> x @fglob 0 '/howard/test/Apr15' 1 '/howard/test/EDI204_blah' 2 '/howard/test/May15' 3 '/howard/test/testing_all_Oct14.tar' 4 '/howard/test/testpm.dat' 5 '/howard/test/testpm2.pl' DB<11> open $fh,'<',$fglob[6] or die $! DB<12>

    A couple things to note:

    1. readdir returned the . and .. links, glob did not
    2. Both returned both directories and files (Apr15 and May15 are directories).
      You will want to test to make sure $file is a file before you try to open it.
    3. readdir returned file name only, glob the whole path. That's why open $fh,'<',$files[6] failed.
      I needed to append the path before opening the file.
    Dum Spiro Spero
Re: Trouble opening and reading file within loop
by tangent (Parson) on Sep 22, 2015 at 13:00 UTC
    @files only contains the names of the files, so you need to prepend the directory path. You can change this bit:
    open my $fh, "<", $file or die "Can't open '$file': $!";
    to...
    open my $fh, "<", "Configs/$file" or die "Can't open '$file': $!";
    Update:
    In light of what GotToBTru says below, and although other ways have already been pointed out, you could add these lines:
    foreach my $file (@files) { next if $file =~ m/^\./; # skip .. and . next unless -f "Configs/$file"; # test to see if a file open my $fh, "<", "Configs/$file" or die "Can't open '$file': $!";