Miscellaneous Topics I

Perl is a very large subject. In this section we cover several things that don't quite merit an entire class with labs, etc. This is more of a survey and overview of things that you might need someday. It is good to at least know that they are there. Give each of them a try and play around a little!

perldoc and POD

Perl has very extensive online documentation. It is all accessed via the 'perldoc' command. For example:
% perldoc perlsyn
will give you a thorough explanation of the statement syntax of Perl. To get a sense of the totality of Perl documentation try this:
% perldoc perltoc
To get the documenation for any built-in function you can do:
% perldoc -f print
This documentation is in a simple format called POD - Plain Old Documentation. See full details with 'perldoc perlpod'. Here is an example of using POD to provide external documentation for your source code. This kind of POD is usually put after a line containing only __END__. Anything after this special END line is ignored when the script is run. It is processed by perldoc.


#!/usr/bin/perl print "hello, world\n"; __END__ =head1 NAME hello =head1 SYNOPSIS This is the B<simplest> of programs.
'perldoc' will show this:
NAME hello SYNOPSIS This is the simplest of programs.
There are several POD commands like =head1. They all begin with =.
A blank line must precede and follow the commands.

split and join

'split' takes a regular expression and a scalar and returns an array.
my $line = "helen:34:f:123 main st:san francisco:90034"; my @fields = split /:/, $line; print "age: $fields[1]\n"; # 34
It does a global search in the scalar for the regular expression and returns the array of the strings BETWEEN the places it matched. Another example:
my $line = "hello, blah, oops , more,t"; my @fields = split /\s*,\s*/, $line; # @fields gets: hello blah oops more t

If you give an empty regex to split it will return an array of all of the individual characters in the string.

my $s = "hello there"; my @chars = split //, $s; print "@chars\n"; # this prints: h e l l o t h e r e

The opposite of split is 'join'.

@nums = (1, 4, 32, 2); $numstr = join ":", @nums; # 1:4:32:2
When an array is interpolated, Perl is using 'join' to put spaces between the elements of the array.

Input Record Separator

The special variable $/ is used to alter how lines are read from a file handle. It tells Perl what string (unfortunately not a pattern/regexp) to use to indicate the 'end of line'. Setting it to something other than the default "\n" allows you to read "paragraphs" or "chunks" at a time rather than lines at a time. Sometimes the unit of a line is not what you really want. For example, Setting it to "\n\n" will cause a program to read until it sees an entirely blank line. It will return a multi-line string with embedded newlines.

Setting $/ to 'undef' allows you to read an entire file in one fell swoop (because you will never encounter the undef value). Here is an example:

my $picture; { local $/; open IN, "pic.jpg" or die; $picture = <IN>; close IN; }
The use of 'local' above will cause $/ to be set to 'undef' for the <IN>. When the block is exited $/ will revert to its former value.

File Tests

Asking whether a file is readable or executable or whether it exists at all is very simple in Perl.
if (-r $fname) { print "$fname is readable\n"; } if (-e $fname) { print "$fname DOES exist\n"; }
There are many letters to put after the dash. Check the documentation at "perldoc -f -X". Useful ones include:
r readable w writable x executable d directory e exists T text file B binary file M time since last modification (in fractional days)

File and Directory Manipulation

Perl's heritage is from the Unix operating system. Nowhere is this more clear than in the names of the built in functions for manipulating files and directories.
chmod chdir mkdir rmdir chown unlink (rm - delete a file) rename (mv)
With these functions you can manipulate files much like you would from the Unix command line. There are also these useful platform independent modules in the standard library shipped with Perl:
File::Copy File::Basename File::Spec File::Find
The last one enables easy recursive perusing of an entire directory structure. See full details with "perldoc File::Find". Here's an example that will lower case the name of all text files in a directory heirarchy:
use File::Find; find(\&lower, '.'); sub lower { rename $_, lc if -f && /\.txt$/; # cryptic or clear? }
By the way, 'lc' is lower case (HEllo => hello), 'uc' is upper case (helLO => HELLO), and 'ucfirst' is first char upper case (helLO => HelLO).

One Liners

At the shell prompt you can try out little snippets of Perl code to see how they work.
% perl -e 'print ucfirst "helLO", $/'
It's best to use single quotes after the -e (e for execute). Note that $/ (see the Input Record Separator above) is a newline by default and is often easier to type than "\n".

Dates and Times in Perl

Perl has several ways to deal with time. The lowest level is this:
my $t = time; # get current time
This is the current time in seconds since January 1, 1970.

For details about day, month, year, etc use localtime.

my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
See "perldoc -f localtime" for details.

There is a function named 'timelocal' that is the inverse of localtime. It lives in a separate module named Time::Local which you need to 'use':

use Time::Local; my $time = timelocal($sec, $min, $hour, $mday, $mon, $year);
See the full details with "perldoc Time::Local".

The module DateTime (and many associated modules) will do everything you ever wanted to do with dates and times. Note, however, this truth:

If you have a date in your program you have a bug in your program.

Random Numbers in Perl

The 'rand' function is all you need for generating random numbers.
my $r = rand 10;
This gives a random real (fractional) number from 0 up to but not including 10. To generate a random dice roll of 1 to 6:
my $roll = int(rand 6) + 1;
The function 'int' will chop off the fractional portion leaving only the integer from 0 to 5.

Or to choose a random element of an array:

my $x = $nums[ rand @nums ];
@nums is in scalar context so will yield the size of the array. And "rand @nums" is in integer context so there will be an implied int( ). If you understand this, you have come a long ways to understanding the idea of context sensitivity in Perl!


There are the functions shift, unshift, push and pop for dealing with the ends of arrays. 'splice' does everything you would ever want in the middle of the array.
my @nums = (1, 3, 5, 7, 9, 11); my @mid = splice @nums, 2, 3, 4, 6; print "@nums\n"; # 1 3 4 6 11 print "@mid\n"; # 5 7 9
See full details with "perldoc -f splice".

Process Management

The 'system' function will execute an external program for you.
system "/usr/bin/tar cvf /tmp/alltxt.tar *.txt";
On Unix you can give it any shell command (including redirection and pipelines).
system "who | grep me | wc -l >/tmp/count";
Two other ways of doing this:
my $count = `who | grep me | wc -l`;
Note the backquotes. They deliver the STDOUT of the process into the scalar.

Or a line by line approach:

open IN, "who|" or die "cannot execute who\n"; while (<IN>) { next unless /me/; ++$count; } close IN;
This special parameter to 'open' effectively executes an arbitrary pipeline and reads from the standard output of it. You can also write to a pipeline. Nice!

The Environment

Environment variables are available to a Perl program by way of the %ENV hash:
# show all environment variables: for my $v (sort keys %ENV) { print "$v\t$ENV{$v}\n"; }
If you put a new value into %ENV it will be seen by any subsequent program you run with 'system' or backquotes.

A Simple Database

Built into Perl is a very nice mechanism for keeping hashes on disk in a simple (but very useful) key-value database.
dbmopen my %ages, "ages", 0644 or die "can't make ages dbm file"; $ages{Jon} = 53; $ages{Pam} = 34; $ages{Harry} = 2;
It looks like this data is going into a normal hash in memory but in actuality it is going to a file on your hard disk!

You can quit the program and later do this:

dbmopen my %ages, "ages", 0644 or die "cannot open ages\n"; print $ages{Harry}; # prints 2!