Fall Semester 2002


Lecture Notes Four: Perl, cgi-bin, CGI, %ENV, QUERY_STRING, GET, more Perl.

We start with Perl.

Let's first write a Perl program and see it run.

(This serves as a quick introduction to Perl).

We're looking at the minimal Perl we need to know to get started with CGI.

So, let's first write a Perl program and see it run.

Use pico, emacs or another editor and create a file called one with the following contents:

#!/usr/local/bin/perl

print "Hello, world!";
This is a complete Perl program, but can you run it already?

Look at the file with the ls command:

burrowww.cs.indiana.edu% ls -l one
-rw-r--r--   1 dgerman  faculty      41 Sep  4 15:16 one
burrowww.cs.indiana.edu% 
(Ask me about my umask value.)

The -rw-r--r-- means that this file can be read and modified by the owner (dgerman) and can be viewed by the groups to which the owner belongs to and by the world. To make this file executable use chmod:

burrowww.cs.indiana.edu% chmod +x one
burrowww.cs.indiana.edu% ls -l one
-rwxr-xr-x   1 dgerman  faculty      41 Sep  4 15:16 one
burrowww.cs.indiana.edu% 
That's another way of changing permissions, not as portable though.

The file is now executable and we can run it:

burrowww.cs.indiana.edu% ./one
Hello, world!burrowww.cs.indiana.edu%
Change the program as indicated below:
#!/usr/bin/perl

print "Hello, world!\n"; 
And run it again
burrowww.cs.indiana.edu% ./one
Hello, world!
burrowww.cs.indiana.edu%
and now we are ready to start our Perl tutorial.

The first line in any Perl script must start with a hash sign (#) followed by an exclamation sign (!) and then by the absolute address of the perl interpreter (which perl).

Perl is located in /usr/local/bin/perl on burrow and on most other Unix systems.

The perl interpreter is, like your web server, just another program.

burrowww.cs.indiana.edu% ls -l /usr/local/bin/perl
-rwxr-xr-x   3 root      1156692 Apr 24  2000 /usr/local/bin/perl
burrowww.cs.indiana.edu%
burrowww.cs.indiana.edu% file /usr/local/bin/perl
/usr/local/bin/perl:    ELF 32-bit MSB executable SPARC Version 1, dynamically linked, not stripped
burrowww.cs.indiana.edu% 
When a Perl script gets invoked the perl interpreter is located and started (using the first line of the script). The interpreter then takes care of the rest of the file which is, in fact, the Perl program.

We will now provide an introduction to Perl through a set of examples.

1. The empty program is a valid program.

burrowww.cs.indiana.edu% vi empty
burrowww.cs.indiana.edu% cat empty
#!/usr/local/bin/perl

burrowww.cs.indiana.edu% chmod +x empty
burrowww.cs.indiana.edu% ./empty
burrowww.cs.indiana.edu% 
2. Scalar variables are identified by a special prefix, the dollar sign. If we wanted to write a program that computes the value of 3 + 5 and stores that in a variable with the name of x it could look like this (to give an example):
burrowww.cs.indiana.edu% vi two
burrowww.cs.indiana.edu% cat two
#!/usr/local/bin/perl

$x = 3 + 5; 
burrowww.cs.indiana.edu% chmod +x two
burrowww.cs.indiana.edu% ./two
burrowww.cs.indiana.edu% 
Of course this program does not communicate much.

To print the value of x we use the print command.

 
burrowww.cs.indiana.edu% vi two
burrowww.cs.indiana.edu% cat two
#!/usr/local/bin/perl

$x = 3 + 5; 
print $x; 
burrowww.cs.indiana.edu% ./two
8burrowww.cs.indiana.edu%
print takes a list of arguments, separated by commas, evaluates them, and then prints the results to the screen in the order in which they appear as arguments. $x evaluates to an integer, gets printed, and then the program terminates. Control then returns to the operating system which prompts the user for more input.

Other things that we could print are characters and strings. Let's look at strings first. We could start by saying that strings are sequences of characters that appear in between double quotes. Thus

"perl"
is a string, and so is
"two words"
or
 "      " 
the last one being a string of exactly 6 blanks.

So we can change the program that computes 3 + 5 to add a blank space after the result.

burrowww.cs.indiana.edu% vi two
burrowww.cs.indiana.edu% cat two
#!/usr/local/bin/perl

$x = 3 + 5; 
print $x, " "; 
burrowww.cs.indiana.edu% ./two
8 burrowww.cs.indiana.edu%
Note the space after the variable, and the comma (,) in between them.

This way the result (8) doesn't get as cluttered as before by the prompt.

In strings delimited by double quotes certain combinations of characters have a special, clearly determined meaning. For example the following group of two characters: \n stands for a carriage return (or newline). So if we change the script that computes the results of 3 + 5 (and prints it out,) to print "\n" instead of " " after the result

#!/usr/local/bin/perl

$x = 3 + 5; 
print $x, "\n";
the output appears on a line of its own:
burrowww.cs.indiana.edu% ./two
8
burrowww.cs.indiana.edu% 
3. Lists can be stored in variables that are prefixed by the symbol @. Here's a program that assigns a list of integers to a variable a. Using the foreach construct, it then goes over the entire list of variables and adds them up, to print the result at the end.
burrowww.cs.indiana.edu% vi three
burrowww.cs.indiana.edu% cat three
#!/usr/local/bin/perl

@a = (1, 2, 3, 4); 

foreach $a (@a) {
  $sum += $a; 
} 

print "The sum is: ", $sum, "\n"; 
burrowww.cs.indiana.edu% chmod +x three
burrowww.cs.indiana.edu% ./three
The sum is: 10
burrowww.cs.indiana.edu%
As they say, by the principle of least surprise the sum variable gets initialized to 0, and so it's 0 the first time it's used. The foreach loop uses a variable $a that takes every value in the list @a in turn; each such value is added to $sum, the statement
$sum += $a;
being a short form of
$sum = $sum + $a;

Lists have their elements in a certain order, and indexed by their position in their list. For example the first element of the list @a has index 0 and can be referred to as $a[0]. Here's program three modified again to print the value of the third element of the list, $a[2]. (Note that the first element having index 0 the third one will have index 2).

burrowww.cs.indiana.edu% vi three
burrowww.cs.indiana.edu% cat three
#!/usr/local/bin/perl

@a = (1, 2, 3, 4); 

print "The third element has value: ", $a[2], "\n"; 
burrowww.cs.indiana.edu% ./three
The third element has value: 3
burrowww.cs.indiana.edu%
A special (and perhaps intimidating) construction gives the index of the last element in list @a:
$#a
This is very useful if the list changes with time (although we won't have such examples in this tutorial). Here's a modified version of three that also prints out the number of elements in the list by using the index of the last element:
burrowww.cs.indiana.edu% vi three
burrowww.cs.indiana.edu% cat three
#!/usr/local/bin/perl

@a = (1, 2, 3, 4); 

print "The third element has value: ", $a[2], "\n"; 

print "The list has ", $#a + 1, " elements.\n"; 
burrowww.cs.indiana.edu% ./three
The third element has value: 3
The list has 4 elements.
burrowww.cs.indiana.edu%
If a list @a is empty, then $#a evaluates to -1.

4. A special list @_ is used to hold parameters passed to functions. Here's the program that uses a subroutine add to add 3 to 5 and then prints the result.

burrowww.cs.indiana.edu% vi four
burrowww.cs.indiana.edu% cat four
#!/usr/local/bin/perl

$x = &add(3, 5); 

print $x, "\n"; 

sub add {
  local ($a, $b) = @_; 
  return $a + $b; 
} 
burrowww.cs.indiana.edu% chmod +x four
burrowww.cs.indiana.edu% ./four
8
burrowww.cs.indiana.edu%
The subroutine is invoked with &. The definition of the subroutine starts with sub.

The two parameters of the functions are called $a and $b. They are local to the add subroutine. The subroutine simply returns the sum of its parameters. The list of parameters passed to the function can be found in @_ which is a list (@) with a curious name: _ (underscore).

5. A simple example of recursion is the definition of fact a subroutine that computes the factorial of an integer. For an integer number n the factorial of n is written n! and is the product of all integers from 1 to n, that is

1 * 2 * ... * (n - 1) * n

So, for example,

5! = 1 * 2 * 3 * 4 * 5 = 120

Here's a simple Perl program that computes the factorial of 5.

burrowww.cs.indiana.edu% vi five
burrowww.cs.indiana.edu% cat five
#!/usr/local/bin/perl

$n = 5; $x = &fact($n); 
print "The factorial of ", $n, " written ", 
      $n, "! is equal to: ", $x, "\n";  

sub fact {
  local ($n) = @_; 
  if ($n == 0) { return 1; }
  else { return $n * &fact($n - 1); } 
} 
burrowww.cs.indiana.edu% chmod +x five
burrowww.cs.indiana.edu% ./five
The factorial of 5 written 5! is equal to: 120
burrowww.cs.indiana.edu% 
The == operator is familiar to any C programmer, and so is the if statement (although in Perl the curly braces are not optional, they are not optional, they are mandatory). Perl does not have booleans. In Perl everything is true, except 0 (zero), "" (the empty string), and the undefined value.

6. That much about passing parameters to a subroutine. Let's now talk about the way one passes parameters to the entire program. A special list is holding those values, and its name is @ARGV. $ARGV[0] is the first command line argument and the index of the last one is $#ARGV, as expected. Here's a Perl program that prints back its command-line arguments:

burrowww.cs.indiana.edu% vi six
burrowww.cs.indiana.edu% cat six
#!/usr/local/bin/perl

foreach $a (@ARGV) {
 print $a, "\n"; 
} 
burrowww.cs.indiana.edu% chmod +x six
burrowww.cs.indiana.edu% ./six a b c
a
b
c
burrowww.cs.indiana.edu% ./six
burrowww.cs.indiana.edu% ./six 1 2 3 4 5 6
1
2
3
4
5
6
burrowww.cs.indiana.edu%
Try ./six -d . for the fun of it.

7. A simple exercise would be to modify the six program to distinguish and signal the situation when there are no command-line parameters passed to the program at all.

burrowww.cs.indiana.edu% vi six
burrowww.cs.indiana.edu% cat six
#!/usr/local/bin/perl

if ($#ARGV >= 0) {
  foreach $a (@ARGV) {
    print $a, "\n"; 
  } 
} else {
  print "No arguments.\n"; 
}
burrowww.cs.indiana.edu% ./six 4 3 2
4
3
2
burrowww.cs.indiana.edu% ./six 
No arguments.
burrowww.cs.indiana.edu%
8. Let's close our tour of Perl by introducing associative arrays. They are a very natural way of associating values with a set of distinct keys. For example we have associated host names and port numbers with usernames. No two usernames in A348/A548 are identical. Each one identifies a unique person. Each person has been assigned a host name and port number.

Here are some fictitious assignments:

LBIRD        blesmol        19900
MJORDAN      bobac          19901
SPIPPEN      degu           19902
TKUKOC       jerboa         19903 
Here's a program that creates two associative arrays indexed by usernames, and when invoked with a username on the command line returns the assignments for the owner of that username.
burrowww.cs.indiana.edu% vi seven
burrowww.cs.indiana.edu% cat seven
#!/usr/local/bin/perl

%hostnames = (
"LBIRD"     =>   "blesmol",        
"MJORDAN"   =>   "bobac"  ,        
"SPIPPEN"   =>   "degu"   ,        
"TKUKOC"    =>   "jerboa"        
);  

%portnumbers = ( 
"LBIRD"     =>   "19900",
"MJORDAN"   =>   "19901",
"SPIPPEN"   =>   "19902",
"TKUKOC"    =>   "19903"
); 

if ($#ARGV >= 0) {
  print $ARGV[0], 
        "'s web server runs on ", 
        $hostnames{$ARGV[0]}, 
        " and uses port #", 
        $portnumbers{$ARGV[0]}, 
        "\n"; 
} else { 
  print "No username specified.\n"; 
} 
burrowww.cs.indiana.edu% chmod +x seven
burrowww.cs.indiana.edu% ./seven LBIRD
LBIRD's web server runs on blesmol and uses port #19900
burrowww.cs.indiana.edu% 
This concludes our first tour through Perl.

In class we are going to discuss the incremental development of the following program that

This is really very easy:

#!/usr/bin/perl
 
while ($x = <STDIN>) {
  ($com, $arg) = split(/ /, $x); 
  print "You have typed: $x"; 
  if ($com =~ /^bye/i) { print "Good-bye!\n"; exit; }
  elsif($com =~ /^add/i) { $acc += $arg; print "Acc is now $acc\n"; }
  elsif($com =~ /^sub/i) { $acc -= $arg; print "Acc is now $acc\n"; } 
  else { print "Acc stays $acc\n"; } 
}
We write this program and run it.

We thus introduce

We later discuss how we can implement something similar with CGI and how the implementation method is fundamentally different. We first return to the helloFive context from the lab.

Last time we developed a simple hello script. Its output was not sophisticated.

The output was coded in HTML, and the only difference between it and a similar (identical, in fact) file was that the output of the script had to start with a label that was specifying what type it had:

Content-type: text/html
followed by a blank line (hence the \n\n that was coming after it).

We then extended this script to allow for variable output from it.

We also need to start talking about the misterious printenv you found in your cgi-bin directory.

Have you looked at it?

And we said associative arrays (hashes) are important in Perl.

The purpose of next week's lectures is to provide enough information to allow you to implement a script with the following functionality for your next assignment (the functionality is described below, and it has two parts). The lectures next week will clarify the second part of that assignment.

Here now is the prototype for the first part of assignment #2.

Notice that just like helloFive:

Unlike helloFive: It will be our purpose to understand the mechanism that makes this possible.

Here's what we will need to get this done:

We'll try to touch all points on this list by developing an example that builds on printenv.

Once that is done, with next week's lectures we will move to:

That will enable us to implement the second part of assignment 2, for which we also have a prototype.

Thus we return to the original calculator that started these notes.

After that we can define CGI, and implement a function readParse that captures that definition.

We now start developing the helper program for the first part of the next assignment.

Here's a starting point:

#!/usr/local/bin/perl

print "Content-type: text/html\n\n";

print "<html><body><pre>"; 

$string = $ENV{'QUERY_STRING'}; 

foreach $key (keys %ENV) {

  if ($key eq $string) { 
    print $key, " --> ";
    print $ENV{$key}, "\n";  
  } else {
    print qq{<a href="/cgi-bin/circular?$key">$key</a>}, "\n"; 
  }

} 

print "</pre></body></html>"; 

Try the script above.

Did you notice the circular in the code above?

What does it mean? What does it do? Why is it there for? What, if anything, is it?

How can we take it out of the script's code without disturbing the functionality of the script?


Last updated: Sep 9, 2002 by Adrian German for A348/A548