You want to get a list of filenames similar to MS-DOS's *.*
and Unix's *.h
(this is called globbing).
Perl provides globbing with the semantics of the Unix C shell through the glob
keyword and < >:
@list = <*.c>; @list = glob("*.c");
You can also use readdir
to extract the filenames manually:
opendir(DIR, $path); @files = grep { /\.c$/ } readdir(DIR); closedir(DIR);
The CPAN module File::KGlob does globbing without length limits:
use File::KGlob; @files = glob("*.c");
Perl's built-in glob
and <WILDCARD> notation (not to be confused with <FILEHANDLE>) currently use an external program to get the list of filenames on most platforms. This program is csh on Unix,[2] and a program called dosglob.exe on Windows. On VMS and the Macintosh, file globs are done internally without an external program. Globs are supposed to give C shell semantics on non-Unix systems to encourage portability. The use of the shell on Unix also makes this inappropriate for setuid scripts.
[2] Usually. If tcsh is installed, Perl uses that because it's safer. If neither is installed, /bin/sh is used.
To get around this, you can either roll your own selection mechanism using the built-in opendir
or CPAN's File::KGlob, neither of which uses external programs. File::KGlob provides Unix shell-like globbing semantics, whereas opendir
lets you select files with Perl's regular expressions.
At its simplest, an opendir
solution uses grep
to filter the list returned by readdir
:
@files = grep { /\.[ch]$/i } readdir(DH);
You could also do this with the DirHandle module:
use DirHandle; $dh = DirHandle->new($path) or die "Can't open $path : $!\n"; @files = grep { /\.[ch]$/i } $dh->read();
As always, the filenames returned don't include the directory. When you use the filename, you'll need to prepend the directory name:
opendir(DH, $dir) or die "Couldn't open $dir for reading: $!"; @files = (); while( defined ($file = readdir(DH)) ) { next unless /\.[ch]$/i; my $filename = "$dir/$file"; push(@files, $filename) if -T $file; }
The following example combines directory reading and filtering with the Schwartzian Transform from Chapter 4, Arrays, for efficiency. It sets @dirs
to a sorted list of the subdirectories in a directory whose names are all numeric:
@dirs = map { $_->[1] } # extract pathnames sort { $a->[0] <=> $b->[0] } # sort names numeric grep { -d $_->[1] } # path is a dir map { [ $_, "$path/$_" ] } # form (name, path) grep { /^\d+$/ } # just numerics readdir(DIR); # all files
Recipe 4.15 explains how to read these strange-looking constructs. As always, formatting and documenting your code can make it much easier to read and understand.
The opendir
, readdir
, closedir
, grep
, map
, and sort
functions in perlfunc (1) and in Chapter 3 of Programming Perl; documentation for the standard DirHandle module (also in Chapter 7 of Programming Perl); the "I/O Operators" section of perlop (1), and the "Filename Globbing Operator" section of Chapter 2 of Programming Perl; we talk more about globbing in Recipe 6.9; Recipe 9.7