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.
use strict; use warnings;
to the top.$#FILES
is one way to have the count of files but as arrays start at 0 you need to add 1 to it – Dakar$#array
for anything other than getting the last index in@array
is complicating things unnecessarily. Why not just use@array
in scalar context? – Itchjoin()
seems unnecessary. Why not justprint scalar(@FILES), "\n";
? – Itch