ElectroRed has asked for the wisdom of the Perl Monks concerning the following question:

Hi I want to open every file in directry. Read every line as an array or as string(and I can convert it to array using split). I have written script(using internet) just to see if I can print all data in file, but it is not opening file. How can it be corrected? Here's code
$dir = "/temp/"; opendir(DIR, $dir) or die "cannot open"; while (defined($file = readdir(DIR))){ open (F, $file) or die "cannot open file"; while (<F>){ print $_; } close F; } closedir(DIR);

Replies are listed 'Best First'.
Re: File handling basics
by GrandFather (Saint) on Jul 17, 2015 at 10:31 UTC

    Aside from your immediate problem, there are a few things that will help write robust file handling code now and in the future.

    First, always use strictures (use strict; use warnings; - see The strictures, according to Seuss).

    Use the three parameter version of open and lexical file handles:

    my $path = "$dir/$entry"; open my $inFile, '<', $path, or die "Can't open '$path': $!\n";

    Checking that an entry is a file before trying to open it will save failures, especially for . and .. directory entries which are present in each directory on common file systems.

    Your code is better written:

    use strict; use warnings; use 5.010; my $startDir = "/temp/"; opendir my ($dir), $startDir or die "cannot open '$startDir'\n"; while (defined (my $entry = readdir $dir)) { my $path = "$dir/$entry"; next if !-f $path; open my $inFile, '<', $path, or die "Can't open '$path': $!\n"; print <$inFile>; }

    Note that lexical file and directory handles close when they go out of scope so explicit closes are not needed in this code. Also note that including file or directory information and the failure mode in a die makes the error reporting much more useful.

    Premature optimization is the root of all job security
Re: File handling basics
by vinoth.ree (Monsignor) on Jul 17, 2015 at 10:07 UTC

    Include $! in die part of open(),It will give you the reason, why it can not open the file.

    Note that the file names in $file do not include the full path and do include "." and ".." which you probably don't care to have. You can eliminate them with,

    @FILES=grep(!/^\.\.?}$/, readdir(DIR));

    The regular expression used in the grep can be extended to filter all sorts of ways, returning only files with a specific extension or starting with some text,etc.

    To check for all the subdirectories in a directory, try code like this:

    $path = shift; $path = "." unless $path; opendir( DIR, $path ) or die "Can't open $path: $!"; while ( $entry = readdir( DIR ) ) { $type = ( -d "$path\\$entry" ) ? "dir" : "file"; # $path is cr +ucial! print "$type\t$entry\n"; } closedir( DIR );

    It's a common mistake to leave out the $path from the -d check. If you do this, perl thinks you're talking about files in the current directory. Since the dirs don't -e in your current directory, they definitely don't -d. Exceptions are . and .., which exist in every directory.

    opendir, redir, closedir offer the most power and flexibility; but if you just want to read the files out of directory, you can also glob them:

    $path = shift; $path .= "/*" while( $name=glob($path) ) { print "$name"; }

    All is well. I learn by answering your questions...
Re: File handling basics
by i5513 (Pilgrim) on Jul 17, 2015 at 10:02 UTC
    You have to add directory path to The name when opennig The file.
    See readdir
      Thanks man. It worked. Now I want to read every line. How I do it?

        If you are successfully opened the files, then your code,should be working.

        open (F, $file) or die "cannot open file"; while (<F>){ print $_; } close F;

        All is well. I learn by answering your questions...
Re: File handling basics
by Laurent_R (Canon) on Jul 17, 2015 at 11:08 UTC
    As already suggested by vinoth.ree, but somewhat buried in a longer post, I would recommend using the glob which makes things easier that the opendir/readdir combination for 2 reasons: glob does not return the . and .. special entries (wgich you don't want to process) and returns files with their path. So you could simply write:
    my $dir = "/temp/"; for my $file (glob "$dir/*") { next unless -f $file; # process only regular files, not dirs open (my $F, $file) or die "cannot open file $!"; while (<$F>){ print $_; } close $F; } # closedir(DIR); -- not needed with glob
    I made a few additional changes reflecting the currently widely accepted good practices.

      I made a few additional changes reflecting the currently widely accepted good practices.

      you missed one ;)
      - open (my $F, $file) or die "cannot open file $!"; + open (my $F), '<', $file or die "cannot open file $!";

      @OP: If for some reason you have a file '>123.txt', then open (my $F, $file) would open the file for writing and truncate it.

      Two purely cosmetical changes would be to replace $F with $fh and also add the filename to the error message, like "cannot open file '$file': $!".

      @OP: I would consider replacing 'die' with 'warn and next', so that you can process the remaining files even if one is not readable to the current user.

      Variant a)

      open (my $F), '<', $file or warn "cannot open file '$file': $!"; defined $F or next;

      Variant b)

      open (my $F), '<', $file or warn "cannot open file '$file': $!" and next;

      I prefer Variant a) because chaining conditionals may lead to surprises. Mostly because 'or' short circuits the evaluation and due to operator precedence.

        you missed one ;)
        Yes, indeed, Monk::Thomas, you're right, I missed one good-practice improvement, and a quite important one. The three-argument syntax for open has been around for quite a number of years and is definitely better than the old two-arguments syntax. Thank you for pointing out this.

      I am also happy to report that a spot check of globdemonstrates no immediate ability to confuse it or get the wrong answers back. Way back in the old days it failed frequently for me in a Windows environment -- I no longer remember how, but suspect the use of drive letters and current directory on each were amongst the issues.

      However, I threw, off the cuff, a bunch of potential curve balls at globrecently and I was very happy to discover it worked perfectly on every test I could think of in 90 seconds.

      Hardly deterministic, but I used to be able to break it without having to work at it, so something has gotten better -- or perhaps I have simply become less warped.

Re: File handling basics
by choroba (Cardinal) on Jul 17, 2015 at 10:03 UTC
    Another programmer has had a similar issue recently: Need Urgent help in perl.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: File handling basics
by i5513 (Pilgrim) on Jul 17, 2015 at 10:02 UTC
    You have to add directory path to The name when opennig The file.
    See readdir
    Please remove this duplicated comment, i'm not sure if I can. I don't know how I published it two times. Sorry