Ïðèãëàøàåì ïîñåòèòü
Ëèòåðàòóðà (lit-info.ru)

A Quick Tour

Previous Table of Contents Next

A Quick Tour

What follows is a quick tour of some of the modules already installed on your system as part of the core Perl distribution.

Exploring Files and Directories

In Hour 10, you learned how to open directories and read lists of filenames contained within those directories. At that time, when the subject of reading subdirectories came up, you were told not to be concerned. Now you'll learn how to traverse directories and subdirectories recursively.

You might write a common program to find a particular file without knowing exactly what directory it's in. For example, suppose you want to find a file named important.doc somewhere beneath the directory documents, as shown here:

A Quick Tour

This illustration shows a directory structure under a parent directory named documents. Finding a file somewhere under documents, using only opendir/readdir/closedir, is not easy. First, documents must be searched for the file. Then each of the directories under documents must be searched—accounting, misc, and personal—and then each directory under those directories, and so on.

This is an old problem, solved again and again by programmers over the last 30 years. Writing your own solution to it would be a waste of time. Naturally, the designers of Perl have included an easy solution: the File::Find module. To use the File::Find module in your program, simply enter the following in your program—preferably somewhere near the top:


use File::Find;


A new function called find then becomes available to you. The find function's syntax is as follows:


find subref, dirlist


The second argument to find is a list of directories to search. The first argument is new to you; it's a subroutine reference. A subroutine reference is created just like a scalar or array reference: It's simply the subroutine name with a backslash in front of it. You must use the & in front of the subroutine name to take a reference to it. The subroutine named will be called for each file and directory found in dirlist.

The program to find the missing important.doc is shown in Listing 14.1.

Listing 14.1. Finding a File

1:   #!/usr/bin/perl -w

2:   use strict;

3:   use File::Find;

4:

5:   sub wanted {   

6:       if ($_ eq "important.doc") {

7:           print $File::Find::name;

8:       }

9:   }

10:  find \&wanted, '/documents';


Lines 1–2: These lines provide the usual start to your Perl programs. Warnings are enabled with -w, and use strict is used to catch errors.

Line 3: The File::Find module is brought into your program. It makes the find function available to you.

Line 5: This function is called for each file and directory under '/documents'. If you have 100 files and 12 directories, this routine will be called 112 times.

Line 6: When the wanted() function is called, $File::Find::name will contain the pathname to the current file being examined and $_ will contain just the name. This line determines whether the filename is important.doc; if so, it prints the full pathname.

Line 10: The find function is called with a subroutine reference—\&wanted—and a directory. The wanted() function is called for each file and directory under '/documents'.

The function called by find will have the following variables available to it:

  • $File::Find::name— The current pathname, directory, and filename.

  • $File::Find::dir— The current directory name.

  • $_— The current filename (without the directory). It's important that you don't change the value of $_ in your function, or if you do, change it back when you're done.

Listing 14.2 contains another File::Find example. This example removes all the files on the C: and D: drives with the extension .tmp; these temporary files accumulate and tend to clutter your hard disk. You can easily adapt this program to remove files from a Unix system or perform any kind of file maintenance.

Listing 14.2. Removing Temporary Files

1:   #!/usr/bin/perl -w

2:   use strict;

3:   use File::Find;

4:

5:   sub wanted {

6:      # Check to see if the filename is not a directory

7:      if ( -f $File::Find::name ) {

8:          # Verify the filename eds in .tmp

9:          if ( $File::Find::name=~/\.tmp$/i) {

10:             print "Removing $File::Find::name";

11:             unlink $File::Find::name;

12:         }

13:     }

14:  }

15:  find(\&wanted, 'c:/', 'd:/');


Most of the program in Listing 14.2 is similar to the one shown in Listing 14.1, with the following differences.

Line 7: The filename passed in is tested to ensure it is a regular file. Remember, this subroutine gets called for both files and directories.

Lines 9–11: The filename is checked to see whether it contains .tmp at the end of the name. If so, the file is deleted with unlink.

Copying Files

Another common task—copying files—can be done the hard way in Perl:

1.
Open the source file for reading.

2.
Open the destination file for writing.

3.
Read the source file and write to the destination.

4.
Close both files.

And, of course, after each step, you must make sure that no errors occurred and that each write was successful. That's the hard way. An easier way is to take advantage of Perl's File::Copy module, which does the file copying for you. The following is an example of this module:


use File::Copy;

copy("sourcefile", "destination") || warn "Could not copy files: $!";


The preceding snippet copies the contents of sourcefile to destination. The copy function returns 1 on success or 0 if a problem occurred, and it sets the variable $! to the proper error.

The File::Copy module also provides a move function,which moves a file from one directory to another. If the file can be moved by simply renaming it, it's renamed; this is usually the case when the source file and the destination file are on the same file system or disk. If simply renaming the file isn't possible, first the file is copied to the destination filename, and then the original file is removed. Consider this example:


use File::Copy;

if (not move("important.doc", "d:/archives/documents/important.doc")) {

    warn "important.doc could not be moved: $!";

    unlink "d:/archives/documents/important.doc";

}


In the preceding snippet, the file important.doc is moved from its current directory to the target directory d:/archives/documents. If the move function fails, it's possible that a partial target file exists. The unlink function removes the partially copied target file if the move fails.

Is Anybody Out There?

Perl's modules aren't limited to just manipulating your files and directories. For example, you can use the Net::Ping module to determine whether your system can communicate properly on a network.

Net::Ping gets its name from a Unix utility called ping, which got its name from the "ping" sound that submarines use in echo location. The ping utility sends a packet to another system on the network. If that system is up and running, it sends a reply, and the ping command reports success. Net::Ping, shown here, works the same way:


use Net::Ping;



if ( pingecho("www.yahoo.com", 15) ) {

    print "Yahoo is on the network.";

} else {

    print "Yahoo is unreachable.";

}


In the preceding snippet, the Net::Ping module provides a function called pingecho. The pingecho function takes two arguments, the first is a host to find—www.yahoo.com in this case. The second argument indicates how long pingecho should wait for a response in seconds.

By the Way

Due to the nature of Perl on Microsoft Windows platforms, the Net::Ping module does not currently work. Net::Ping relies on the alarm function, which does not work under Windows. ActiveState, a major developer of Perl for Windows, has announced plans to implement many missing functions for Windows and may incorporate those changes into Perl eventually.


Once Again, in English?

The English module allows some of Perl's obscure special variables to be addressed by more verbose names, as in this example:


use English;



while(<>) {

    print $ARG;

}


In the preceding snippet, while(<>) normally reads a line of input from STDIN and assigns it to $_. It still does. With use English, however, the variable $_ is also known as $ARG. A partial list of special variables and their English equivalents is shown here:

Special Variable

English Name

$_

$ARG

@_

@ARG

$!

$OS_ERROR

$^O

$OSNAME

$0

$PROGRAM_NAME


You can find a full list of special variables and the English equivalents in the English module's online documentation.

More Diagnostics

The Perl module diagnostics can help you find bugs in your program. As you were typing in the examples from this book, the Perl interpreter may have emitted an error message that you didn't quite understand. For example, the short program


#!/usr/bin/perl -w



use strict;

print "For help, send mail to help@support.org\n";


causes Perl to emit this warning message:


In string, @support now must be written as \@support at line 4

Global symbol "@support" requires explicit package name at line 4


The diagnostics module causes Perl to emit a wordy explanation of its errors and warnings. You can change the sample program to include the diagnostics module like this:


#!/usr/bin/perl -w

use strict;

use diagnostics;



print "For help, send mail to help@support.com\n";


The revised program causes a wordier diagnostic message to print:


In string, @support now must be written as \@support at line 4

Global symbol "@support" requires explicit package name at ./diag.pl line 5 (#1)



    (F) You've said "use strict vars", which indicates that all variables

    must either be lexically scoped (using "my"), or explicitly qualified to

    say which package the global variable is in (using "::").


If you think for a moment about the two messages, it's apparent that they're related. The first message is obvious. Perl wants your email message to be written as help\@support.com. The second message, now that it's been explained, is a bit more obvious: Because use strict was in effect, the @support variable should have been declared with my. Of course, @support isn't a variable; it was supposed to be part of an e-mail address, but Perl was misled by the special character @.

The letter in front of the message indicates what kind of error you have. (W) indicates a warning, (D) indicates you've used a deprecated syntax, (S) is a severe warning, and (F) is a fatal error. For all message types except (F), your Perl program will continue to run.

Perl has 60 pages of descriptions to its error messages. If you're having problems figuring out Perl's terse error messages, use diagnostics can sometimes help.

Did you Know?

The full list of error messages and diagnostic information is available for you to brow
Previous Table of Contents Next