Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Count number of files in a folder with Perl

Tags:

perl

I would like to count the number of files inside a folder with Perl. With the following code I can list them, but how can I count them in Perl?

$dir = "/home/Enric/gfs-0.5.2016061400";
opendir(DIR, "$dir");
@FILES = grep { /gfs./ } readdir(DIR);
foreach $file (@FILES) {
    print $file, "\n";
}
closedir(DIR);
like image 450
Enric Agud Pique Avatar asked Jun 14 '16 20:06

Enric Agud Pique


People also ask

How do I count the number of files in a folder?

Browse to the folder containing the files you want to count. Highlight one of the files in that folder and press the keyboard shortcut Ctrl + A to highlight all files and folders in that folder. In the Explorer status bar, you'll see how many files and folders are highlighted, as shown in the picture below.

How do I count how many files are in a folder in Linux?

The easiest way to count files in a directory on Linux is to use the “ls” command and pipe it with the “wc -l” command. The “wc” command is used on Linux in order to print the bytes, characters or newlines count. However, in this case, we are using this command to count the number of files in a directory.


4 Answers

If you want to just count them, once you have a directory open for reading you can manipulate context so that readdir returns the list of all entries but then assign that to a scalar. This gives you the length of the list, ie. the number of elements

opendir my $dh, $dir;
my $num_entries = () = readdir($dh);

The construct = () = imposes list context on readdir and assigns (that expression) to a scalar, which thus gets the number of elements in that list.‡ §   See it in perlsecret. Also see this page.

There are clearer ways, of course, as below.

If you want to count certain kinds of files, pass the file list through grep first, like you do. Since grep imposes the list context on its input readdir returns the list of all files, and after filtering grep itself returns a list. When you assign that to a scalar you get the length of that list (number of elements), ie. your count. For example, for all regular files and /gfs./ files

use warnings;
use strict;

my $dir = '/home/Enric/gfs-0.5.2016061400';
opendir my $dh, $dir  or die "Can't open $dir: $!";

my $num_files =  grep { -f "$dir/$_" } readdir($dh);
rewinddir($dh);  # so that it can read the dir again
my $num_gfs   =  grep { /gfs./ } readdir($dh);

(This is only an example, with rewinddir so that it works as it stands. To really get two kinds of files from a directory better iterate over the entries one at a time and sort them out in the process, or read all files into an array and then process that)

Note that readdir returns the bare filename, without any path. So for most of what is normally done with files we need to prepend it with the path (unless you first chdir to that directory). This is what is done in the grep block above so that the -f file test (-X) has the correct filename.

If you need to use the file list itself, get that into an array and then assign it to a scalar

# Get the file list, then its length
my @files_gfs = map { "$dir/$_" } grep { /gfs./ } readdir($dh);
my $num_gfs = @files_gfs;

Here map builds the full path for each file. If you don't need the path drop map { }. Note that there is normally no need for the formal use of scalar on the array to get the count, like

my $num_gfs = scalar @files_gfs;    # no need for "scalar" here!

Instead, simply assign an array to a scalar instead, it's an idiom (to say the least).

If you are processing files as you read, count as you go

my $cnt_gfs = 0;
while (my $filename = readdir($dh)) {
    $cnt_gfs++ if $filename =~ /gfs./;
    # Process $dir/$filename as needed
}

Here readdir is in the scalar context (since its output is assigned to a scalar), and it iterates through the directory entries, returning one at a time.

A few notes

  • In all code above I use the example from the question, /gfs./ -- but if that is in fact meant to signify a literal period then it should be replaced by /gfs\./

  • All this talk about how readdir returns bare filename (no path) would not be needed with glob (or then better File::Glob), which does return the full path

    use File::Glob ':bsd_glob';  # (better with this)
    
    my @files = glob "$dir/*";
    

    This returns the list of files with the path $dir/filename.

    Not that there is anything wrong with opendir+readdir. Just don't forget the path.

    Yet another option is to use libraries, like Path::Tiny with its children method.


The assignment () = readdir $dh itself returns a value as well, and in this case that whole expression (the assignment) is placed in the scalar context.

The problem is that many facilities in Perl depend in their operation and return on context so one cannot always merely assign what would be a list to a scalar and expect to get the length of the list. The readdir is a good example, returning a list of all entries in list context but a single entry in scalar context.

§ Here is another trick for it

my $num_entries = @{ [ readdir $dh ] };

Here it is the constructor for an anonymous array (reference), [], which imposes the list context on readdir, while the dereferencing @{ } doesn't care about context and simply returns the list of elements of that arrayref. So we can assign that to a scalar and such scalar assignment returns the number of elements in that list.

like image 144
zdim Avatar answered Oct 05 '22 14:10

zdim


You have the list of files in @FILES. So your question becomes "how do I get the length of an array?" And that's simple, you simply evaluate the array in scalar context.

my $number_of_files = @FILES;
print $number_of_files;

Or you can eliminate the unnecessary scalar variable by using the scalar() function.

print scalar @FILES;
like image 35
Dave Cross Avatar answered Oct 05 '22 14:10

Dave Cross


Try this code for starters (this is on Windows and will include . , .. and folders. Those can be filtered out if you want only files):

#!/usr/bin/perl -w

my $dirname = "C:/Perl_Code";
my $filecnt = 0;

opendir (DIR, $dirname) || die "Error while opening dir $dirname: $!\n";
while(my $filename = readdir(DIR)){
     print("$filename\n");
     $filecnt++;
}
closedir(DIR);
print "Files in $dirname : $filecnt\n";
exit;
like image 29
tale852150 Avatar answered Oct 05 '22 14:10

tale852150


I know this isn't in Perl, but if you ever need a quick way, just type this into bash command line:

 ls -1 | wc -l 

ls -1 gives you a list of the files in the directory, and wc -l gives you the line count. Combined, they'll give you the number of files in your directory.

Alternatively, you can call bash from Perl (although you probably shouldn't), using

system("ls -1 | wc -l"); 
like image 28
JDY Avatar answered Oct 05 '22 15:10

JDY