Flexible and Powerful: Perl

Programming in C offers considerable flexibility, as you saw in the preceding lesson, but C was originally designed for system-level programming tasks. Perl, by contrast, was designed to be a powerful language for string or word processing. You can most easily see the difference by exploring a programming task.

Task 22.1: Exchange, a Demonstration Currency Translator Written in Perl

Some friends recently returned from a couple of weeks' traveling throughout Europe. Upon their return, they commented that they were baffled by the sheer variety of currencies. They're not the first to observe this, of course, but I thought “what a great Perl program to write!”


Translating currencies is simple once you get the formula and exchange rates. Here in the United States, currency values are usually presented relative to a single US dollar, so the French franc might be valued at 3.381, which means that every dollar you exchange is worth 3.381 francs. Exchange $20 and it will net you 20*3.381, or 67.62FF.

The Exchange program started out life by reading in the current exchange rates for five major world currencies (US dollar, Japanese yen, German deutsche mark, French franc and British pound), prompting for an amount in US dollars, and then showing what its equivalent value was in these other currencies. Much more useful, however, is the capability to translate from any one of these currencies to the other four. Further, there are sites on the Internet that show the daily currency exchange rate, so ensuring up-to-date rates is another desirable program feature.

Let's see how to carry this out with Perl and UNIX.

  1. The basic logic flow (or algorithm if you want to be technical about it) for the Exchange program is this:

    								Read current exchange rates
    								Repeat
    								  Ask user for an amount and currency
    								  Translate that into US dollars
    								  Show the equivalent value in all five currencies
    								Until done
    							
  2. Perl supports subroutines to help develop clean, readable programs, so let's have a peek at the main code in the program:

    #!/usr/local/bin/perl
    
    &read_exchange_rate;     # read the exchange rate table into memory
    
    # now let's cycle, asking the user for input...
    
    print "Please enter the amount, appending the first letter of the name of
    ";
    print "the currency that you're using (franc, yen, deutschmark, pound) -
    ";
    print "the default value is US dollars.
    
    ";
    print "Amount: ";
    
    while (<STDIN>) {
    
      ($amnt,$curr) = &breakdown(chop($_));
    
      $baseval = $amnt * (1/$rateof{ $curr});    # translate currency into USD
      printf("%2.2f USD, ", $baseval * $rateof{'U'});                # USA
      printf("%2.2f Franc, ", $baseval * $rateof{'F'});              # France
      printf("%2.2f DM, ", $baseval * $rateof{'D'});                 # Germany
      printf("%2.2f Yen, and ", $baseval * $rateof{'Y'});            # Japan
      printf("%2.2f Pound
    
    Amount: ", $baseval * $rateof{'P'});    # England
    }
    

    The first line needs to point to your Perl interpreter; an easy way to find it is to use which perl on the command line.

    I've added some fancy output formatting with the print statement, but otherwise this code is quite similar to the algorithm you've already seen. Notice that the array rateof uses the first letter of the currency as an index and returns the current exchange rate relative to the US dollar (that is, $rateof{'F'} is actually 3.381).

    One slick thing about Perl is that subroutines can return a list of variables, as you see demonstrated in the call to the “breakdown” subroutine, which returns both $amnt and $curr.

  3. Two subroutines are included in this program, read_exchange_rate and breakdown. Let's consider them in reverse order.

    The breakdown subroutine receives the user currency entry and splits it into two parts: the numeric value and the first letter of the currency indicator, if any (the default currency is US dollars):

    sub breakdown {
       @line = split(" ", $_);         # chop at space
    
       $amnt = $line[0];
       if ($#line == 1) {
         $curr = $line[1];
         $curr =~ tr/a-z/A-Z/;         # uppercase
         $curr = substr($curr, 0, 1);  # first char only
       } else { $curr = "U"; }
       return ($amnt, $curr);
    }
    

    I won't go into too much detail here—Perl can be somewhat overwhelming when you first start working with it—but if the subroutine is given a value such as “34.5 yen” it will return $amnt = 34.5 and $curr = 'Y'.

  4. The current exchange rate is read in from an associated data file, exchange.db, which contains the exchange rate for the five currencies (although the exchange rate for US dollars is always 1, of course!):

    sub read_exchange_rate {
      open(EXCHRATES, "<exchange.db") ||
        die "Can't find current exchange rates.
    ";
    
      while ( <EXCHRATES> ) {
        chop; split;
        $curr = @_[0];
        $val  = @_[1];
        $rateof{$curr}  = $val;
      }
      close(EXCHRATE);
    }
    

    Using the notation you're already familiar with from the UNIX shell, Perl specifies whether files are being opened for reading or writing with the direction of the redirect arrow. Here on line two you can see that the exchange.db file is being opened for reading. The || die is a shorthand way of saying, if the open fails, output the error message and quit immediately.

    The exchange.db data file looks like this:

    P       0.594
    D       1.687
    F       5.659
    Y       132
    U       1
    
  5. Here's the cool part about this program: The exchange rates can be lifted off a handy Web page automatically, to ensure that they're always current and accurate.

    I accomplish that task by using the lynx text-based Web browser to grab a page off Yahoo Financials that has the exchange rates, then a few simple UNIX commands in a pipeline to strip out the information I don't want and reformat it as needed.

    It's all dropped into a shell script, build-exchrate:

    #!/bin/sh
    
    # Build a new exchange rate database by using the data on Yahoo
    
    # special case for the British Pound needed...
    
    lynx -dump http://quote.yahoo.com/m3?u | 
       awk '/U.K./ { if (NF > 4) print "P	"$3 } '
    
    # now let's get the other three...
    
    lynx -dump http://quote.yahoo.com/m3?u | 
       awk '/Can/,/SFran/ { if (NF > 4) print $1"	"$2 } ' | 
       cat -v | sed 's/M-%/Y/' | 
       egrep '(U.K.|DMark|FFranc|Yen)' | 
       sed 's/U.K./P/;s/DMark/D/;s/FFranc/F/;s/Yen/Y/'
    echo "U 1"
    exit 0
    

    To learn more about the useful lynx command, use man lynx on your system. It's a good addition to your bag of UNIX tricks.

    The output of this command is exactly the database file format, albeit to the screen rather than the data file:

    % build-exchrate
    P       0.594
    D       1.687
    F       5.659
    Y       132
    U       1
    

    Creating the data file for the program is easy:

    % build-exchrate > exchange.db
    %
    

For those of you who are Web developers, lynx users are why you've been putting those ALT tags in your pages whenever you included any sort of images with IMG SRC. Try it and you'll see what I mean.


  1. Let's now try out the exchange program and see how it works!

    % perl exchange.pl
    Please enter the amount, appending the first letter of the name of
    the currency that you're using (franc, yen, deutschmark, pound) -
    the default value is US dollars.
    
    Amount: 20
    20.00 USD, 113.18 Franc, 33.74 DM, 2640.00 Yen, and 11.88 Pound
    
    Amount: 20 pounds
    33.67 USD, 190.54 Franc, 56.80 DM, 4444.44 Yen, and 20.00 Pound
    
    Amount: 20 yen
    0.15 USD, 0.86 Franc, 0.26 DM, 20.00 Yen, and 0.09 Pound
    
    Amount: 20 deutschmark
    11.86 USD, 67.09 Franc, 20.00 DM, 1564.91 Yen, and 7.04 Pound
    
    Amount: 20 franc
    3.53 USD, 20.00 Franc, 5.96 DM, 466.51 Yen, and 2.10 Pound
    

    Finally, one last query: In the United States the dynamite RedHat Linux package (a great, inexpensive UNIX for PC-based computers) costs about $50. It's easy to use Exchange to compute the equivalent price overseas:

    Amount: 50
    50.00 USD, 282.95 Franc, 84.35 DM, 6600.00 Yen, and 29.70 Pound
    
    Amount:
    

    To quit the program, I use ^D to send an end-of-file signal.

You can get an online copy of the Exchange program and its companion build-exchrate shell script by visiting http://www.intuitive.com/tyu24/.


The Exchange program demonstrates how you can write succinct and sophisticated programs in Perl. It also demonstrates that Perl can be a wee bit confusing if you're uninitiated. That's why your best bet for learning Perl, or any other programming language, is to spend the time to find and read a good tutorial.


More importantly, the program demonstrates that it's the combination of tools—UNIX commands and Perl— that lets you really create some terrific applications.

Task 22.2: Checking Code Quality with -w

There are two main ways to run Perl programs: by typing perl followed by the name of your program (as shown previously), or by specifying them directly on the command line. For the latter approach to work, you'll need to include #!/usr/local/bin/perl as the first line of your program and use chmod to make your program executable.


Whichever way you choose to invoke your Perl program, the Perl interpreter will scan the program to see whether it all makes syntactic sense, and then actually begin executing the instructions specified.

The scan performed is rudimentary, however, and catches only the most grievous of mistakes. Add a simple -w flag, however, and the interpreter looks much more closely at the program, emitting various warnings (“-w” = “warnings”) for odd constructs and more.

Even Perl programs that work fine can generate quite a variety of warnings! In fact, perl -w is the Perl version of lint.

  1. I'll start by making the exchange.pl program executable, to save a little bit of typing:

    % chmod +x exchange.pl
    %
    

    Not much output, but no output is good news: Everything worked fine.

  2. I'm going to delete the semicolon after the call to read_exchange_rate in the exchange.pl file so that you can see what happens when the Perl interpreter finds the mistake.

    Done. (We'll call that an “edit between the lines,” okay?)

    % exchange.pl
    syntax error at ./exchange.pl line 7, near "print"
    Execution of ./exchange.pl aborted due to compilation errors.
    

    Hmm…line 7, eh? Let's use the -n flag to the cat command to see the first 10 lines, numbered:

    % cat -n exchange.pl | head
         1  #!/usr/bin/perl
         2
         3  &read_exchange_rate      # read exchange rate into memory
         4
         5  # now let's cycle, asking the user for input...
         6
         7  print "Please enter the amount, appending the first letter of the name of
    ";
         8  print "the currency that you're using (franc, yen, deutschmark, pound) -
    ";
         9  print "the default value is US dollars.
    
    ";
        10  print "Amount: ";
    

    Line 7 isn't where the problem occurs (it's on line 3), but this is a great opportunity to point out that you should never entirely trust the line numbers in compiler or interpreter error messages.

  3. Now let's invoke Perl with the -w flag to see whether it offers more advice on what's wrong with the program:

    % perl -w exchange.pl
    syntax error at exchange.pl line 7, near "print"
    Scalar value @_[0] better written as $_[0] at exchange.pl line 43.
    Scalar value @_[1] better written as $_[1] at exchange.pl line 44.
    Execution of exchange.pl aborted due to compilation errors.
    %
    

    Alas, no help here, but it is showing two old-style lines I have in the read_exchange_rate subroutine.

  4. I'm going to restore the semicolon (though I won't show that here; just use vi to add it) and run the -w flag one more time to see whether there are any additional useful suggestions:

    % perl -w exchange.pl
    Scalar value @_[0] better written as $_[0] at exchange.pl line 43.
    Scalar value @_[1] better written as $_[1] at exchange.pl line 44.
    Use of implicit split to @_ is deprecated at exchange.pl line 42.
    Use of implicit split to @_ is deprecated at exchange.pl line 42.
    Use of implicit split to @_ is deprecated at exchange.pl line 42.
    Use of implicit split to @_ is deprecated at exchange.pl line 42.
    Name "main::EXCHRATE" used only once: possible typo at exchange.pl line 47.
    Use of uninitialized value at exchange.pl line 45, <EXCHRATES> chunk 6.
    Use of uninitialized value at exchange.pl line 45, <EXCHRATES> chunk 6.
    Please enter the amount, appending the first letter of the name of
    the currency that you're using (franc, yen, deutschmark, pound) -
    the default value is US dollars.
    
    Amount:
    

    Wow! Lots of output, most of which is telling me that there are new, fancier ways to specify things (for example, use $_[1] instead of @_[1]).

  5. Buried in all of this output, however, is a bug in the program that the Perl interpreter found:

    Name "main::EXCHRATE" used only once: possible typo at exchange.pl line 47.
    

    A closer look at the read_exchange_rate subroutine shows what's wrong:

    % cat -n exchange.pl | tail -12
        37  sub read_exchange_rate {
        38    open(EXCHRATES, "<exchange.db") ||
        39      die "Can't find current exchange rates.
    ";
        40
        41    while ( <EXCHRATES> ) {
        42      chop; split;
        43      $curr = @_[0];
        44      $val  = @_[1];
        45      $rateof{ $curr}  = $val;
        46    }
        47    close(EXCHRATE);
        48  }
    

    Can you see the problem it has found? The open statement creates a file handle called EXCHRATES, which is then used in the while statement, but when I went to close the file handle, I forgot the trailing s and called it EXCHRATE.

    An easy fix, fortunately!

Even the most carefully written Perl programs can have problems lurking. The -w flag isn't ideal, but you should become familiar with its use and learn how to distinguish important warnings from unimportant ones.


In this case the bug identified wouldn't have broken anything or generated any incorrect results, but if I had continued to improve Exchange, not closing the file handle could have become a significant problem down the road.

Task 22.3: Online Perl Documentation and Information

Earlier I recommended that you buy a good Perl tutorial book to learn more about the language. I'm going to revise that a bit, because the standard Perl installation includes a ton of online documentation.


  1. If you've been trying all the examples as you've been reading the lessons, you're already familiar with the standard UNIX man page format and how to find the information you see therein. Man pages are good for summaries of how to work with individual commands, but they're much less useful for explaining large, complex programs such as the C shell, the Elm Mail System, or the Perl interpreter.

    That's why the Perl documentation is broken into a staggering number of man pages:

    % man -k perl | grep 1
    a2p (1)              - Awk to Perl translator
    perl (1)             - Practical Extraction and Report Language
    perlLoL (1)          - Manipulating Lists of Lists in Perl
    perlXStut (1)        - Tutorial for XSUBs
    perlapio (1)         - perl's IO abstraction interface.
    perlbook (1)         - Perl book information
    perlbot (1)          - Bag'o Object Tricks (the BOT)
    perlbug (1)          - how to submit bug reports on Perl
    perlcall (1)         - Perl calling conventions from C
    perldata (1)         - Perl data types
    perldebug (1)        - Perl debugging
    perldelta (1)        - what's new for perl5.004
    perldiag (1)         - various Perl diagnostics
    perldoc (1)          - Look up Perl documentation in pod format.
    perldsc (1)          - Perl Data Structures Cookbook
    perlembed (1)        - how to embed perl in your C program
    perlfaq (1)          - frequently asked questions about Perl
    perlfaq1 (1)         - General Questions About Perl
    perlfaq2 (1)         - Obtaining and Learning about Perl
    perlfaq3 (1)         - Programming Tools
    perlfaq4 (1)         - Data Manipulation
    perlfaq5 (1)         - Files and Formats
    perlfaq6 (1)         - Regexps
    perlfaq7 (1)         - Perl Language Issues
    perlfaq8 (1)         - System Interaction
    perlfaq9 (1)         - Networking
    perlform (1)         - Perl formats
    perlfunc (1)         - Perl builtin functions
    perlguts (1)         - Perl's Internal Functions
    perlipc (1)          - Perl interprocess communication (signals, fifos, pipes, safe subprocesses, sockets, and semaphores)
    perllocale (1)       - Perl locale handling (internationalization and localization)
    perlmod (1)          - Perl modules (packages and symbol tables)
    perlmodlib (1)       - constructing new Perl modules and finding existing ones
    perlobj (1)          - Perl objects
    perlop (1)           - Perl operators and precedence
    perlpod (1)          - plain old documentation
    perlre (1)           - Perl regular expressions
    perlref (1)          - Perl references and nested data structures
    perlrun (1)          - how to execute the Perl interpreter
    perlsec (1)          - Perl security
    perlstyle (1)        - Perl style guide
    perlsub (1)          - Perl subroutines
    perlsyn (1)          - Perl syntax
    perltie (1)          - how to hide an object class in a simple variable
    perltoc (1)          - perl documentation table of contents
    perltoot (1)         - Tom's object-oriented tutorial for perl
    perltrap (1)         - Perl traps for the unwary
    perlvar (1)          - Perl predefined variables
    perlxs (1)           - XS language reference manual
    s2p (1)              - Sed to Perl translator
    POSIX (3)            - Perl interface to IEEE Std 1003.1
    

    Quite a few man pages, eh?

  2. The good news (I think) is that the standard perl man page offers a suggested order for reading the man pages that can help overcome some of the gasping, drowning feeling you probably have right now!

    For ease of access, the Perl manual has been split up into a number of sections:

        perl        Perl overview (this section)
        perldelta   Perl changes since previous version
        perlfaq     Perl frequently asked questions
    
        perldata    Perl data structures
        perlsyn     Perl syntax
        perlop      Perl operators and precedence
        perlre      Perl regular expressions
        perlrun     Perl execution and options
        perlfunc    Perl builtin functions
        perlvar     Perl predefined variables
        perlsub     Perl subroutines
        perlmod     Perl modules: how they work
        perlmodlib  Perl modules: how to write and use
        perlform    Perl formats
        perllocale  Perl locale support
        
        perlref     Perl references
        perldsc     Perl data structures intro
        perllol     Perl data structures: lists of lists
        perltoot    Perl OO tutorial
        perlobj     Perl objects
        perltie     Perl objects hidden behind simple variables
        perlbot     Perl OO tricks and examples
        perlipc     Perl interprocess communication
    
        perldebug   Perl debugging
        perldiag    Perl diagnostic messages
        perlsec     Perl security
        perltrap    Perl traps for the unwary
        perlstyle   Perl style guide
    
        perlpod     Perl plain old documentation
        perlbook    Perl book information
        perlembed   Perl ways to embed perl in your C or C++ application
        perlapio    Perl internal IO abstraction interface
        perlxs      Perl XS application programming interface
        perlxstut   Perl XS tutorial
        perlguts    Perl internal functions for those doing extensions
        perlcall    Perl calling conventions from C
    
    (If you're intending to read these straight through for
    the first time, the suggested order will tend to reduce
    the number of forward references.)
    

    I find the online documentation overwhelming, so don't worry if this doesn't make you want to leap online and read it all.

  3. The smarter way to learn more about Perl is to read the online documentation. You can start at http://www.perl.org/ or jump straight to the terrific Perl 5 Desktop Reference in HTML form at http://reference.perl.com/guides/perl5.html.

Start with the FAQs and the basic Perl man page, and then graduate to a book on the subject (or even a course)—you'll be a Perl expert.


Task 22.4: Other Useful Perl Commands

There are useful pieces to the Perl environment other than just the -w flag to the interpreter! In this section I'll highlight some special command flags worth knowing to help you get the most out of Perl.


  1. The first new flag to learn about is the -d debug flag. It's documented (in detail) in perldebug, which you'll want to read because various debugging commands are accessible.

  2. An interesting variation in debugging requires another flag, the -e (execute the following command) flag. It will let you actually use the Perl interpreter interactively:

    $ perl -de 1
    
    Loading DB routines from perl5db.pl version 1
    Emacs support available.
    
    Enter h or `h h' for help.
    
    main::(-e:1):   1
      DB<1> print "Hi!";
    Hi!
      DB<2> q
    %
    

    The 1 was actually a command to the Perl interpreter, and it was because I specified -d for debugging that the interpreter executed the command and then stopped for input.

  3. If you'll be using Perl to write Common Gateway Interface (CGI) scripts for a Web server (which I'll talk about again in the next hour), you'll want to explore the -T (“taint”) flag, which keeps close track of the flow of user input for security. See perlsec for more information.

  4. Finally, no discussion of Perl can be complete without highlighting the terrific Perl developer community and its Comprehensive Perl Archive Network (CPAN). The best place to learn about it is http://www.perl.com/CPAN-local/CPAN.html.

You can also use Perl interactively to learn about the CPAN modules available, though I've never had any luck with it myself. Try entering perl -MCPAN -e shell.


The Perl language is well worth spending some time learning. The UNIX shell offers various capabilities, but you'll undoubtedly hit the edges as you become more sophisticated; that's where Perl can really fill in the gaps.


You can find a great list of Perl books—many with reviews included—maintained by Perl guru Tom Christiansen at http://www.perl.com/perl/critiques/index.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.131.72