Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 491
  • Last Modified:

perl script to get file names and counts of sub directories

Hi,
 I am beginner in perl. My goal is to get the file names and counts og sub directories from a directory.
For example my directory structure looks like

 New folder/ Sub folder 1/ sub folder 2/sub folder 3...
 sub folder 1/ file 1/file2/file3..
sub folder 2 / file 1/ file 2/file 3..


 I am copying the updated script below. when I am trying to run the script it is not giving the files count in sub folders but it is giving the counts of sub folders.

#!/usr/local/bin/perl

use strict;
use warnings;

my $dir= 'c:\Documents and Settings\user\Desktop\new folder';

my $directory_count=0;
my $file_count=0;
my $file =0;



my $outfile = 'log.txt';

open my $OUTF, '>', $outfile or die "can't create logfile;$!";

opendir (DIR, $dir);

my @files = readdir(DIR);


   closedir( DIR ) ;

 
   foreach my $files(@files)
   {
   
       
   
    if (-d "$dir/$file")
     
     {
         $directory_count++;
   }
   
     else {
     
    $file_count++;
   
    print {$OUTF} "$files|";
   
    print { $OUTF} "$directory_count |";
   print  { $OUTF}   "$file_count \n";
     
    }
   
   }
   
 Required Output;

  sub folder 1  |  no. of files in sub folder 1
 sub folder 2   | no.of files in sub folder 2...


Thanks,
 




0
new_perl_user
Asked:
new_perl_user
  • 11
  • 8
  • 2
  • +1
1 Solution
 
ozoCommented:

    if (-d "$dir/$file")
     
     {
         $directory_count++;
         print  { $OUTF}   "$file | " .. (() = <$dir/$file/*>) . "\n" ;
   }
0
 
new_perl_userAuthor Commented:
Hi,
  Thanks for the reply but It did not make any change when I tried the above suggestion.

 The output is in this way:

 Sub Folder 1  |  1
Sub Folder  2  |   2..

 I mean it is listing out the sub folder names but instead of counting the files in sub folders. it is still taking the count of sub folders.
0
 
ozoCommented:
#sorry, that should have been . not ..
 print  { $OUTF}   "$file | " . (() = <$dir/$file/*>) . "\n" ;
0
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
new_perl_userAuthor Commented:

Hi,
 I am sorry to say that but still it is doesn't make any difference in my output .
0
 
ozoCommented:
#it looks like $file and $files were meant to be the same
#either change

  foreach my $file(@files)
 
#or

  if (-d "$dir/$files")
     
     {
         $directory_count++;
         print  { $OUTF}   "$files | " . (() = <$dir/$files/*>) . "\n" ;
   }
0
 
new_perl_userAuthor Commented:
Hi,
 Made some improvement but it is showing the wrong counts. I mean some sub folders does not have any files in them so it should be zero, but it is showing all the counts as 5 for every sub folder. I am pasting the new code after making changes according to the suggestions.

#!/usr/local/bin/perl

use strict;
use warnings;

my $dir= 'c:\Documents and Settings\user\Desktop\new folder';

my $directory_count=0;
my $file_count=0;
my $file =0;



my $outfile = 'log.txt';

open my $OUTF, '>', $outfile or die "can't create logfile;$!";

opendir (DIR, $dir);

my @files = readdir(DIR);


   closedir( DIR ) ;

 
   foreach my $file(@files)
   {
   
       
   
    if (-d "$dir/$file")
     
     {
         $directory_count++;
        print  { $OUTF}   "$file | " . (() = <$dir/$file/*>) . "\n" ;
   }
   
     else {
     
    $file_count++;
   
    print {$OUTF} "$file|";
   
    print { $OUTF} "$directory_count |";
   print  { $OUTF}   "$file_count \n";
     
    }
   
   }

Output:
  .  | 3
 .. | 28
Sub Folder 1  | 5
Sub Folder 2 | 5
sub folder 3  | 5...

Thanks,

 
0
 
ozoCommented:
The spaces in $dir and $file may be confusing the glob
Try <\Q$dir/$file\E/*>
I'm not sure if that works in mswindows

print  { $OUTF}   "$file | " , <\Q$dir/$file\E/*> , "\n" ;
#should list the files in the folder instead of counting them so you can see what it is getting
#note , instead of .
0
 
new_perl_userAuthor Commented:
Hi,
 It is not working. I mean it is listing all the files  in that location regardless of my declared directory. Is there any other approach in perl  I could do this regardless of the way I was trying.

Thanks,
0
 
ozoCommented:
What files is it listing?
0
 
new_perl_userAuthor Commented:

Hi,
  For example the directory I declared is at the desktop location. So it is listing all the files that are saved on my desktop and the directory I declared in my script . It is also not giving the counts.

Thanks,
0
 
ozoCommented:
Is that when $file is . or ..
0
 
new_perl_userAuthor Commented:
Hi,
 When I tried the above solution  print  { $OUTF}   "$file | " , <\Q$dir/$file\E/*> , "\n"  it printed all the filenames regardless of the directory declared.

When  I modified back to  print {$OUTF} "$file|".(()= <$dir/$file/*>). "\n";  it  is printing the filenames correctly but the counts are wrong as I said earlier.  Please help if there is any other way.

Thanks,
0
 
wilcoxonCommented:
This should give you what you want (plus sub-dir count which I'm not sure you want but it's easy to change if you don't want it).

Let me know if this doesn't do what you want.
#!/usr/local/bin/perl
use strict;
use warnings;

my $dir= 'c:\Documents and Settings\user\Desktop\new folder';

my $directory_count=0;
my $file_count=0;
my $file =0;

my $outfile = 'log.txt';

open my $OUTF, '>', $outfile or die "can't create logfile;$!";
print {$OUTF} "dir | dir cnt | file cnt\n";

process($dir);

sub process {
    my ($dir) = @_;
    opendir (DIR, $dir);
    my @ents = readdir(DIR);
    closedir( DIR ) ;
    my @dirs = grep { -d "$dir/$_" and not /^\.+$/ } @ents;
    my $cnt = scalar grep { -f "$dir/$_" } @ents;
    print {$OUTF} "$dir | " . scalar(@dirs) . " | $cnt\n";
    foreach my $sub (@dirs) {
        process("$dir/$sub");
    }
}

Open in new window

0
 
new_perl_userAuthor Commented:
Hi,
 When I am trying to execute the above code it is showing up the below errors.  

readdir() attempted on invalid dirhandle DIR at count.pl line 21.
closedir() attempted on invalid dirhandle DIR at  count.pl line 22

Thanks,
0
 
ozoCommented:
opendir (DIR, $dir) or warn "opendir($dir) failed because $!";
0
 
new_perl_userAuthor Commented:
Hi,
 Still the same problem after making changes according to the above suggestion.
0
 
ozoCommented:
It reports no warnings?

0
 
new_perl_userAuthor Commented:
Hi,
 I am sorry. It is working now a small code error i  did, @specifying the directory location. It is almost  close to the output I want  as shown below. This is the output I am getting when I am executing the above script. But  I need a small change  it should not traverse into files , I mean into file2345, file 567.... please.

Current:
dir | dir cnt | file cnt
c:\Documents and Settings\user\Desktop\new folder | 4 | 0
c:\Documents and Settings\user\Desktop\new folder/sub folder 1 | 2 | 0
c:\Documents and Settings\user\Desktop\new folder/sub folder 1/file2345 | 0 | 0
c:\Documents and Settings\user\Desktop\new folder/sub folder 1/file567 | 0 | 0
c:\Documents and Settings\user\Desktop\new folder/sub folder 2 | 0 | 0
c:\Documents and Settings\user\Desktop\new folder/sub folder 3 | 0 | 0
c:\Documents and Settings\user\Desktop\new folder/sub folder 4 | 0 | 0

Required:
 dir cnt  | file cnt
sub folder 1 | 2
sub folder 2 | 0..

Thanks,
 
0
 
wilcoxonCommented:
The code I posted does not traverse into files (only dirs).  Could you please post your current script so that I can try to spot the problem?

I'm unclear on your required output.  The labels say "dir cnt" and "file cnt" but the data lines look like they are "dir name" and "file cnt".  Do you want "dir cnt" in the output or is the label wrong (and it should be "dir name").
0
 
new_perl_userAuthor Commented:
Hi,
 I am so sorry it was the label mistake. I want the sub- directory name and count of sub directories in the sub directory. For example there is a Main directory and in that there are several sub directories and each sub directory contains so many sub directories again.

 Main directory/ sub directory 1/ directory1, directory 2, directory 3...
                           subdirectory 2 / directory 1, directory 2, directory 3...
 
  So the requirement is  the script should traverse into this directory get the sub directory name( sub directoryr 1) and count  of directories in that sub directory( directory1, directory 2, directory 3) . The script which was given by you in the previous reply is the one I am using now. It is doing almost what I want but

1) it is also traversing into the directories and getting their dir names(sub folder 1/file2345)  which is not required
2) In the output it is always giving out the  Main directory path(c:\Documents and Settings\user\Desktop\new folder) which is not required.  

  I  tried to explain it clearly but in case if it is still confusing please let me know. I am attaching an image so that you can better understand  the directory structure although I am not good at drawing.
This is the script I am currently using.

#!/usr/local/bin/perl
use strict;
use warnings;

my $dir= 'c:\Documents and Settings\user\Desktop\new folder';

my $directory_count=0;
my $file_count=0;
my $file =0;

my $outfile = 'log.txt';

open my $OUTF, '>', $outfile or die "can't create logfile;$!";
print {$OUTF} "dir | dir cnt | file cnt\n";

process($dir);

sub process {
    my ($dir) = @_;
    opendir (DIR, $dir);
    my @ents = readdir(DIR);
    closedir( DIR ) ;
    my @dirs = grep { -d "$dir/$_" and not /^\.+$/ } @ents;
    my $cnt = scalar grep { -f "$dir/$_" } @ents;
    print {$OUTF} "$dir | " . scalar(@dirs) . " | $cnt\n";
    foreach my $sub (@dirs) {
        process("$dir/$sub");
    }
}


Dircetory-Structure.JPG
0
 
Carl BohmanCommented:
With minor modifications to your script, I was able to come up with the following (attached).  I believe it does what you are looking for.  It allows you to control the search depth (with a $basedepth of zero being only the base directory, a $basedepth of one goes one level deep, etc.).
#!/usr/local/bin/perl
use strict;
use warnings;

my $basedir= 'c:\Documents and Settings\user\Desktop\new folder';
my $basedepth = 1;

my $directory_count=0;
my $file_count=0;
my $file =0;

my $outfile = 'outfile.txt';

open my $OUTF, '>', $outfile or die "can't create logfile;$!";
print {$OUTF} "dir | dir cnt | file cnt\n";

process($basedir, $basedepth);

sub process {
    my ($dir, $depth) = @_;
    return unless ($depth >= 0);
    opendir (DIR, $dir);
    my @ents = readdir(DIR);
    closedir( DIR ) ;
    my @dirs = grep { -d "$dir/$_" and not /^\.+$/ } @ents;
    my $cnt = scalar grep { -f "$dir/$_" } @ents;
    my $displaydir = ($dir eq $basedir ? $dir : substr($dir, length($basedir)));
    print {$OUTF} "$displaydir | " . scalar(@dirs) . " | $cnt\n";
    foreach my $sub (@dirs) {
        process("$dir/$sub", $depth - 1);
    }
}

Open in new window

0
 
new_perl_userAuthor Commented:
Hi,
when I executed the above script the output was:

o/p:
 
dir | dir cnt | file cnt
 c:\documents and settings\user\new folder | 4 |0
/ sub directory1  | 2 |  0
/sub directory2   | 4 |  0
/ sub directory 3  | 0 |  0
/ sub directory 4  | 2 |  0

  But if possible can I get something like  the one shown below. c:\documents and settings\user\new folder | 4 |0  is the main directory which is not required  Because this output file should be loaded into database so i am trying to get only the required info.

 sub directory1  |  2
sub directory2   |  4
sub directory 3  | 0
sub directory 4  |  2


Thanks,
0
 
Carl BohmanCommented:
This one allows for setting a minimum and maximum depth.  In all honesty, making it this configurable is likely unnecessary, but maybe it will help you learn a bit more about how Perl works to do it this way.  Also note that the way the subdirectory is displayed is configurable by editing the like that sets $displaydir.
#!/usr/local/bin/perl
use strict;
use warnings;

use File::Basename;

my $basedir= 'c:\Documents and Settings\user\Desktop\new folder';
my $basemindepth = 1;
my $basemaxdepth = 1;

my $directory_count=0;
my $file_count=0;
my $file =0;

my $outfile = 'outfile.txt';

open my $OUTF, '>', $outfile or die "can't create logfile;$!";
print {$OUTF} "dir | dir cnt | file cnt\n";

process($basedir, $basemindepth, $basemaxdepth);

sub process {
    my ($dir, $mindepth, $maxdepth) = @_;
    return unless ($maxdepth >= 0);
    opendir (DIR, $dir);
    my @ents = readdir(DIR);
    closedir( DIR ) ;
    my @dirs = grep { -d "$dir/$_" and not /^\.+$/ } @ents;
    my $cnt = scalar grep { -f "$dir/$_" } @ents;
    my $displaydir = basename($dir);
    print {$OUTF} "$displaydir | " . scalar(@dirs) . " | $cnt\n" if ($mindepth <= 0);
    foreach my $sub (@dirs) {
        process("$dir/$sub", $mindepth - 1, $maxdepth - 1);
    }
}

Open in new window

0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 11
  • 8
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now