If you have any experience on UNIX, you've certainly found a need for the find command, which is useful to search for filenames throughout the file system. In particular, you can use wildcards to match filenames and recursively traverse any directory structure (where permissions allow). The UNIX find command can also execute other commands on the files it finds.
The File::Find module within Perl encompasses the same functionality and also gives you the advantage of programmatic structures. To show how this works, I'll walk you through a sample script that employs the File::Find module.
A simple example
This simple Perl script can help you clean up your PC hard drive by finding any files that end with .tmp, .chk, or .zip or that begin with the ~ symbol. (You can see the entire script in Listing A.) The script will print the full path of each file it finds, and a tally of the number of bytes being consumed will appear at the end. You can run the script on either Windows or UNIX if Perl has been installed. Note that in a UNIX environment, you must modify the first line of the script: Change the /bin/perl path to match the path to Perl in your environment. For this example, I will assume that you are running it in a Windows environment.
Of course, Microsoft’s GUI Find utility offers a portion of this functionality. But I wrote the script because once you have the file within Perl, you can do all sorts of things with it, such as open it up and look for a particular pattern, automatically delete it, or use it as input for another application.
I make use of one module within the standard Perl Library and one Perl function, so all of the necessary modules should be available when you install Perl on your Windows machine. (I grabbed Perl from ActiveState.) The File::Find function mimics the UNIX find command and will traverse a file tree. Here's the API for the method:
Find(\&yoursubroutine, ‘dir1’, ‘dir2’…);
You provide the subroutine, which I will detail later, and a list of directories where you want the search to be conducted. Remember that these directories will be traversed in a depth-first fashion.
The other method I use is the stat() function (similar to the C library function of the same name), which gives you all sorts of information about the filename it takes as an argument. Listing B shows the API for the method.
Notice that the function returns the values in a list. The only value we're interested in is $size, which contains the size in bytes of the file given.
All of the work will be performed for the utility in the subroutine. Remember that it will be called each time a file is encountered, so it's our job to determine whether the filename matches the files we're looking for.
The File::Find method has special variables available that will be populated with certain information, as shown here:
- · $_ contains the current filename within the directory
- · $File::Find::dir contains the current directory name
- · $File::Find::name contains $File::Find::dir/$_
When the subroutine is called, you will actually be in the directory in the variable $File::Find::dir . As you can see in Listing C, our subroutine uses regular expression matching on $_ using an if statement to look for all of the filenames we detailed earlier.
If the filename stored within $_ (the default pattern-searching space) matches any of the five regular expressions in the if statement, we will enter the block of code below it. The regular expressions are quite simple. The “\.” indicates a literal dot rather than the special meaning “.” in the world of regular expressions. We use the “\” character to escape the special meaning. The “$” indicates a match at the end of a string and the “^” matches the beginning of the line. Table A maps the files we are trying to match with their corresponding regular expressions.
Note that the script looks for both the lowercase tmp and the uppercase TMP. For the sake of efficiency, you can uppercase the filename and check for only the TMP match.
Finally, the script employs the stat() function to tally the number of bytes being used by all of the files that match one of the conditions in the if statement. If the condition is met, the script stores the value within $size and adds it to the $ByteCount tally variable, as shown in the following code snippet:
$ByteCount += $size;
To run the script on your machine, type the following command in a DOS command prompt window:
This assumes that you have modified your PATH variable to include the Perl executable and that you have saved the utility in a file called Diskrpt.pl. The output of the command will appear in the DOS command prompt window.
In future articles, I will walk through modifications of the script, such as deleting certain file types or feeding the results to another application.
Share some Perl wisdom
If you would like to submit a handy Perl script to Builder.com, please contact the editor.