http://www.perlmonks.org?node_id=1012201

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

Hi

I have a folder which contains a bunch of text files. Opening a text file is easy enough, but I am struggling to open a folder using Perl.

I am pretty sure I must use File::Find though. I read about it in Perl intro but it is too confusing for a novice like me.

All I want is to open my folder an iterate through all the text files within it while doing some basic calculations within each text file, but opening the folder is where I am struggling.

The directory of the file is C:\Users\ZB\Desktop\Text Files. I have a Windows OS.

Any help would be appreciated!

Replies are listed 'Best First'.
Re: Open a folder
by choroba (Cardinal) on Jan 08, 2013 at 09:34 UTC
    You can achieve your goal even without "opening" the folder:
    for my $file (glob 'C:/Users/ZB/Desktop/Text\ Files/*.txt') { calculate($file) }
    Update: Note the slashes separate the path elements and the backslash quotes the space.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Open a folder
by vinoth.ree (Monsignor) on Jan 08, 2013 at 10:37 UTC
    Opening the directory

    To open the directory,use a function called "opendir". You use this same like the open function to open files. Below example shows opening of /tmp directory.

    #!/usr/bin/perl use strict; use warnings; my $tmp_dir="/tmp"; opendir (DIR, $tmp_dir) or die $!;
    Reading the directory

    To read the files and directories in the directory we use the readdir function. readdir returns the name of each file or directory in the opened directory in turn when used in scalar context, or a list of the names of all files and directories in that directory when used in list context. This means that we can use readdir in a foreach loop or any other loop

    while (my $file_name = readdir(DIR)) {print "$file_name\n";}
    Closing the directory

    We use the function closedir to close the directory once we are finished with it. Like files, the directory will be closed when the program finish, but sometimes need to close the directory opened

    closedir(DIR);

      Hi

      This is the first progress I've made in some time, so thank you very much.

      When I run your code I get a list of all the file names within my directory.

      However I want open these text files as well, and then do some basic calculations(I have put these in a sub) in them. How can I iterate through all the text files within the directory and run my sub on each file?

        With the example you've been given there's a print statement within a while loop. Replace the print statement with the actions you wish to be taken, checking it's a text file, performing the operations you want. http://learn.perl.org, tutorials.

        Since you mentioned File::Find ... Change the sub _SizeAndMD5 to suit your problem.

        #! use strict; use warnings; use File::Find; use Digest::MD5::File; no warnings "File::Find"; use Smart::Comments; local *wanted=sub { if (-d $File::Find::name) { # directory } elsif ($File::Find::name =~ m{\.txt$}i) { # a file of interest print "for $File::Find::name - @{[_SizeAndMD5($File::Find::nam +e)]}\n"; } }; # wanted # Do something based on the file's content local *_SizeAndMD5=sub { my ($filename_S)=@_; return (-e $filename_S ? sprintf("%8.8lx-%32.32s",-s $filename_S,D +igest::MD5::File::file_md5_hex($filename_S)) : undef); }; # _SizeAndMD5: File::Find::find({ no_chdir=>1,wanted=>\&wanted },@ARGV); exit;

        This will process all of the files with a ".txt" extension below the directories specified by @ARGV.

        Hi Dr Manhattan

        Below code process each files in the /tmp directory. If any directory is inside the /tmp directory will not be processed

        Add additional checking on file, like file size and readable permissions to avoid error.

        #!/usr/bin/perl use strict; use warnings; my $tmp_dir="/tmp/"; opendir (DIR, $tmp_dir) or die $!; while (my $file_name = readdir(DIR)) { my $abs_path = $tmp_dir.$file_name; unless(-d $abs_path) #Ignore if it is a directory { &Process_EachFile($abs_path); # Do call a function and process + each file } else { print "$abs_path is a directory\n"; } } closedir(DIR); sub Process_EachFile { my ($filename) = @_; print $filename; open (FH,'<',"$filename") or die $!; while(my $each_line=<FH>) { print $each_line."\n"; #Read file content here and do your calculation. } }
Re: Open a folder
by sen (Hermit) on Jan 08, 2013 at 09:34 UTC
Re: Open a folder
by nithins (Sexton) on Jan 08, 2013 at 10:03 UTC

    use opendir function , it will help i belive for example

    #!/usr/bin/perl use strict; use warnings; my $dir ="./files"; opendir ( DIR, $dir ) || die "Error in opening dir $dir\n"; while( (my $filename = readdir(DIR))){ print "$filename\n"; open (FH,'<',"./files/$filename"); while(<FH>){ ##do your task here} }

      Your use of a global filehandle 'FH' inside a loop isn't a great idea. If the open fails then the nested while loop will use the already open handle FH which was left open to whatever file was opened last. Also since you've taken the trouble to define $dir why not use it when you open the file?

      open (my $fh,'<',"$dir/$filename") or die " ... $!";

      Another thing to watch out for is the directories '.' and '..'. Your code will pass these into $filename and attempt to open them. Use the file test -d $filename to skip directories. Or use -T $filename to find text files and ignore everything else.

      A final suggestion is to use indentation inside loops and control blocks to help you keep track of the open and close curly braces.

Re: Open a folder
by blue_cowdawg (Monsignor) on Jan 08, 2013 at 14:04 UTC
        but I am struggling to open a folder using Perl.

    Dear Monk,
    First, excuse the professor in me, let me make a minor correction to the terminology you are using. What you refer to as a "folder" is actually a directory and Perl provides a wonderful function for reading directories. Here is some sample code to help you along your way:

    #!/usr/bin/perl -w use strict; my $dirname="/path/to/the/directory"; opendir(DIR,$dirname) or die "$dirname:$!"; while (my $entry=readdir(DIR)){ next if $entry eq '.'; next if $entry eq '..'; next if -d $entry; # see notes below | do something with this... } exit(0);
    the -d $entry is to prevent you from attempting to do something unintended with a subdirectory under the desired directory. After you get past all the next logic (which could have been combined in one statement) you then add your code to actually do something with the file names you would get as a result of the rest of the code.

    If you want to recursively operate on that directory here is an example of that code well modified:

    #!/usr/bin/perl -w use strict; workTheDirectory("/path/to/the/directory"); exit(0); # # sub workTheDirectory { my $dirname=shift; opendir(DIR,$dirname) or die "$dirname:$!"; while (my $entry=readdir(DIR)){ next if $entry eq '.'; next if $entry eq '..'; if ( -d $entry ) { my $newdir = $dirname . "/" . $entry; #grow the path workTheDirectory($newdir); } | This is a file... | work it. } return; }
    Clear as mud?


    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg

      Hi, thank you for the help. I am not completely sure what your code is does, but the

      next if $entry eq '.'; next if $entry eq '..';

      did help. I have combined your code with something of another user to come up with this:

      #!/usr/bin/perl -w use strict; use warnings; open (Output, ">Output.txt") or die "Can't open"; my $tmp_dir= 'C:\Users\ZB\Desktop\Text Files'; opendir (DIR, $tmp_dir) or die $!; while (my $file_name = readdir(DIR)) { next if $file_name eq '.'; next if $file_name eq '..'; print "$file_name\n"; } close (Output) closedir(DIR);

      All it does is print all the file names in my directory as output(so at least I am on the right path). However I want to iterate through each text file and do some basic calculations(all in 1 sub) within each file. Any ideas?

        see perldoc -f push and Tutorials (below the text explaining how to/when to post a new tut) or Super Search re iterating through arrays.

        IOW, collect all the relevant $file_name entries into an array; at that point, iterating thru the individual files requires only that you attempt to inform yourself thru study of the references above.

        Update: Of course (/me says belatedly), you'll need to consider executing your script from an appropriate directory or troubling yourself to learn about perldoc -f chdir, perldoc cwd, etc.

            However I want to iterate through each text file and do some basic calculations(all in 1 sub) within each file.

        Add the sub to your code thusly:

        | in your while loop invoke: process_file($tmp_dir,$file); | and then after your loop sub process_file { my($path,$node)=@_; my $fname = $path . '\' . $node; open FIN,"<$fname" or die "$fname:$!"; while (my $line=<FIN>){ chomp $line; | do something here. } }


        Peter L. Berghold -- Unix Professional
        Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
      The advantage of your "correction" is clear. However, note that "folder" is the correct terminology for that structure in windows. The OP has consistently explained the problem from an application view.
      Bill
        BillKSmith
        Correct terminology, perhaps, if you buy (hook, line and sinker) M$'s (non-standard) terminology as an acceptable standard.
            "folder" is the correct terminology for that structure in windows.

        The term "folder" is correct for any GUI environment that represents directories as such. At the system level where I live it's a directory.


        Peter L. Berghold -- Unix Professional
        Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
Re: Open a folder
by Anonymous Monk on Jan 08, 2013 at 10:50 UTC

    #!/usr/bin/perl --
    use Cwd;
    use File::Find::Rule;
    my @dirs = ( cwd() );
    my @textfiles = find( 'file', 'name', qr/\.txt$/i, 'maxdepth', 1, 'in', \@dirs );