July 7, 2004

SysAdmin to SysAdmin: Approaching Perl

Author: Brian Jones

Writing an article about getting started with Perl is a bit like writing an
article about getting started with neurosurgery. Sure, I could cover some
basics -- but it would only leave you scratching your head the first time
something comes up that you've never seen. I'd hate to see a neurosurgeon in
that position! But no matter what you do, there are always times when you just don't know an answer. A much more horrible fate to suffer is to have no clue
where to go to find the answer. So, this article covers one or two
basics of Perl, just to show you that's it's not so scary, and a future article
will tell you where to find solutions when you're on your own.

Many of the uninitiated who first see Perl immediately become intimidated by
the arcane symbols, and strings that look as though a cat walked across the
keyboard. If you're one of these people, fear no more. I myself was one of
those people. Yet even I am now able to conquer common sysadmin problems large
and small, using nothing more than a few lines of Perl. On occasion, I'm even
impressed at the feats of greatness that Perl and I can pull off. In the
end, sysadmins like Perl because Perl makes them look good.

Ah, but where to start? For me, the easiest way to get started is to "scratch
an itch," so to speak. If you don't have anything urgent, porting old shell scripts to Perl provides a great exercise for learning how things are done in Perl. But of course, it helps if
you have some clue of how the world looks through Perl-tinted glasses, so let's
have a look at some basics.

What all the funny symbols mean

Perl uses lots of quirky symbols that look intimidating at first. However, once
you understand what they mean, you'll realize that they are there
to help you. These first three are used to indicate that a particular variable is of
a particular type:

$scalar
@array
%hash

Any time you see a single "$" followed by a name, it's a scalar variable. A
scalar variable can hold any type of value. Strings, numbers, floating point
numerics, and even references to locations in memory can all be stored in
scalar variables. These are probably the most frequently used variable types,
since practically anything can be stored in them.

A single "@" indicates that the variable is an array. I tend to think of arrays
as nothing more than a potentially random selection of scalar values. They are
indexed numerically for you by Perl so that you can easily reference individual bits
within the array later in your script. This one needs an example:


@days = ( 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday');

Once I've declared my array and populated it, referencing individual values is
as easy as asking for it by its index value. Index
numbers start at "0," so if I'm looking to get back "Tuesday," I'd put the line
print $days[2]; in my script.

A single "%" in front of a variable name signifies that it is a hash. A hash is
just slightly more complicated than an array, but affords you the flexibility
of assigning your own index, instead of using the default numeric ones Perl
generates for you. So, suppose I need an array of user names, and an array of
Unix shells, but I need the shells to be matched up with the right users. Instead of
using two arrays and writing the code to match up values from them, I can just
make the username the index for my hash, and make the shell of choice the
value:


%usershell = ( jimi => '/bin/bash', eric => '/bin/tcsh', eddie => '/bin/csh',
billy => '/bin/zsh' );

So now, if I want to know the shell assigned to "eric", I can just ask for it
(almost) by name: print $usershell{'eric'};.

More symbol quirkiness, clarified

Why did I use a "%" to assign a hash, and a "$" when I wanted to print
a value from it? This is initially confusing. Notice also that I did the same
thing with our arrays, above. The reason is that, when I ask for a
particular value of a hash or an array, I'm asking for a piece of scalar data,
not an entire hash or array. Because I want a scalar value back, I use the "$,"
which as we discussed before indicates a scalar value.

The observant will then ask, "Well, you said that the symbols indicate the type
of variable it is -- how do I know whether you're asking for a scalar value from a
hash or an array?" The answer lies in the type of brackets used to ask for the
value. As you can see in the above examples, the curly braces are used for hash
values, and square brackets are for arrays.

But what if you have to reference something that you haven't explicitly defined
in your script? Symbols to the rescue! Probably the most common symbol that I
haven't covered yet is $_. This is sort of a placeholder or
"default" variable used, for example, inside a subroutine to reference the
arguments passed to it. Since you don't know what's being passed to a
subroutine when you write it, you can just use this variable to represent the
parameters. Sometimes, $_ can be implied -- for example, if you
ever see a single line of Perl code which says, simply print;, the
$_ is actually implied, though it's not incorrect to put it there if
it makes things clearer for your brain.

Loops in Perl

I've coded in numerous languages, ranging from C/C++ to PHP and even a little
Python. For loops, I still like Perl the best (though PHP 4 is almost identical
in this respect). While Perl has a rather impressive array of looping
structures, for brevity, I'll cover just two common loops: for and while.

The "for" loop is famous for allowing you to control iteration based either on
an arbitrary number or value you invent, or on a value derived from somewhere else
in your code. Here's a simple for loop that uses an arbitrary number I've
created to determine the number of times the loop executes:


for ($count = 1; $count
{
    print "$count\n";
}

This loop simply prints the numbers 1-10, with a line break after each
number. Of course it's really quite rare that you can just
make up a number like that. Generally, you want the number of loop
executions to be based on some dynamic variable so you don't have hard-coded
constants strewn about your code that may need to be changed at some point
(causing you work, thereby defeating the purpose of scripting in the first
place!). Here's something more realistic:


@users = ('eric', 'billy', 'eddie', 'yngwie');
for ($i=0; $i
{
   &nbsp print "$users[$i]\n";
}

In the above example, I started with an array containing four values. I
initialized $i to zero. Each time through the loop, I
check and see if $i is less than the highest index value in the
@users array. If it is, I keep going, incrementing $i
by 1 each time through. Note that I snuck yet another symbol in here:
$#, which returns the highest index value in an array. Since
arrays are indexed starting at 0, the value should always be the number of
values in the array, minus one.

The while loop

The while loop tests the input, and then executes statements based on the
outcome of the test. This is often useful in situations where input is read
from a file or from the keyboard, and you want to act upon each piece of input
without knowing ahead of time what it is or how much of it there will be.
Here's an example:


open (PASSWD, '</etc/passwd') or die "Can't open file: $!";
while (defined($acct = <PASSWD>)) {
  print "$acct";
}

This does essentially the same thing as cat /etc/passwd would do at the
command line. The first line opens the /etc/passwd file and assigns a handle
name to it called PASSWD. Then, I assign a variable to the value coming in
from the handle (basically, a line of data from the /etc/passwd file), and if
there is a value there, the loop executes. As soon as there ceases to be data
in $acct, the loop stops executing.

In conclusion

Don't say I've teased you; I've not even written enough about Perl to
accomplish that. I'd need another 5,000 words to feel like I've given you even
half of a clue. However, I do hope that what I presented here will make the
task of reading and writing some simple Perl code a little less daunting.
If demand dictates, I'll continue in a future article with more advanced topics
and regular expressions.

Category:

  • Perl
Click Here!