A Gentle Stroll Through Perl

The following collection of code and comments illustrates the basics of coding Perl programs. The stroll begins with a simple "Hello World" script and adds more complex elements as we go. The comments are in bold and the Perl code is plain text.

The content is based heavily on 'A Stroll Through Perl' from LEARNING PERL, 1st edition, in a highly-condensed form and was originally used for a whirlwind, 30-minute presentation to GR.pm newbies. For in-depth, user-friendly explanations and additional examples, we highly recommend the books listed at the bottom of the page.




Program start & end (#!/usr/local/bin/perl & exit)
(lets script find perl interpreter on Unix systems; not needed on Win32...handled by 'file types')
 #!/usr/local/bin/perl   # Pound-sign (#) is a comment sign; rest of line is not processed...except on magic line!
 exit;     # Causes program to exit (duh!).  All statements must end with a semi-colon, newlines are ignored




Output (print)
 #!/usr/local/bin/perl
 print "Hello, PerlMongers!\n";  # '\n' is newline character
 exit;




Input & variable assignment (<STDIN>, =)
 #!/usr/local/bin/perl
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";  # No newline so answer will be on same line, immediately following output string
 $name = <STDIN>;   # Assigns user-typed response to variable $name; variables start with $; "filehandles" bracketed by <>
 chomp $name;    # Strip the newline, if one exists...else, do nothing
 print "Hello, $name!  Nice shirt!\n"; # DOUBLE-quotes replace variables with their value...SINGLE-quotes do not!
 exit;




Conditionals & comparisons (if, else, eq, ==)
 #!/usr/local/bin/perl
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )   # 'eq' is string comparison; '==' is numeric comparison; parens optional but good practice
 {     # Each block of code in a loop or a conditional is surrounded by curly-brackets
  print "Cool name, come on in!\n";
 }     # End of 'if' conditional
 else
 {     # Beginning of 'else' conditional
  print "Welcome, $name!\n";
 }     # End of 'else conditional
 exit;




Advanced conditionals (elsif)
 #!/usr/local/bin/perl
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )
 { print "Cool name, come on in!\n"; } # Brackets can be on the same line; sometimes makes the code more readable
 elsif ( $name eq '' )   # 'elsif', not 'elseif' or 'else if'; no 'case' statement in Perl
 { print "Fine, don't tell me...\n"; }
 else
 { print "Welcome, $name!\n"; }
 exit;




Variable assignment
 #!/usr/local/bin/perl
 $password = '19MAY99';      # If you don't know this date, we don't want to talk to you! : )
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )
 { print "Cool name, come on in!\n"; }
 elsif ( $name eq '' )
 { print "Fine, don't tell me...\n"; exit; }    # Now program exits if name was not supplied
 else
 {
  print "What's your password, $name? ";   #
  chomp ($userpass = <STDIN>);    #
  if ( $userpass eq $password )    #
  { print "Darth Vader lives...come on in!\n"; }  #
  else       #
  { print "I thought I felt the good in you, but...\n"; exit; } #
 }
 print "Welcome to Norad.  Do you want to play a game?\n"; #
 exit;




Arrays & loops (@, for)
 #!/usr/local/bin/perl
 @password_list = ('19MAY99','GR.pm','Perl rules'); # Arrays begin with '@'
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )
 { print "Cool name, come on in!\n"; }
 elsif ( $name eq '' )
 { print "Fine, don't tell me...\n"; exit; }
 else
 {
  print "What's your password, $name? ";
  chomp ($userpass = <STDIN>);
  for $onepass ( @password_list )   # Executes loop once for each value in @password_list, storing the value in $onepass
  {
   if ( $userpass eq $onepass )
   { $good_pass = 1; }   # 1 is interpreted by Perl as "true"; null, undefined & zero are "false"
  }
  if ( $good_pass )    # If previous 'if' statement ever resolved true, this will be true, otherwise undefined or "false"
  { print "Darth Vader lives...come on in!\n"; }
  else
  { print "I thought I felt the good in you, but...\n"; exit; }
 }
 print "Welcome to Norad.  Do you want to play a game?\n";
 exit;




Hashes (%)
 #!/usr/local/bin/perl
 %password_list =     # Hashes begin with %
   (
   'Ben' => 'Kenobi',  # Hashes are built of name/value pairs: KEY => (contains) VALUE
   'Darth' => 'Vader',
   'R2D2' => 'C3PO'
   );
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )
 { print "Cool name, come on in!\n"; }
 elsif ( $name eq '' )
 { print "Fine, don't tell me...\n"; exit; }
 else
 {
  print "What's your password, $name? ";
  chomp ($userpass = <STDIN>);
  if ( $userpass ne '' &&    # Boundary condition; Unknown user + no password = false positive; ALWAYS DEBUG!
   $userpass eq $password_list{ $name } ) # Prefacing hash name with '$' & supplying KEY in curly brackets returns VALUE
  { $good_pass = 1; }    # Now $good_pass is set if the appropriate value is supplied for the key
  if ( $good_pass )
  { print "Darth Vader lives...come on in!\n"; }
  else
  { print "I thought I felt the good in you, but...\n"; exit; }
 }
 print "Welcome to Norad.  Do you want to play a game?\n";
 exit;




Modularizing With Subroutines (&, sub)
 #!/usr/local/bin/perl
 %password_list =
   (
   'Ben' => 'Kenobi',
   'Darth' => 'Vader',
   'R2D2' => 'C3PO'
   );
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )
 { print "Cool name, come on in!\n"; }
 elsif ( $name eq '' )
 { print "Fine, don't tell me...\n"; exit; }
 else
 {
  if ( &passwordOK( $name ) )   # subroutine calls start with '&', accept args in parentheses
  { print "Darth Vader lives...come on in!\n"; }
  else
  { print "I thought I felt the good in you, but...\n"; exit; }
 }
 print "Welcome to Norad.  Do you want to play a game?\n";
 exit;

 sub passwordOK     # Subroutine definitions begin with 'sub' and subroutine name
 {       # Subroutine code contained in curly brackets
  my $name = shift;    # Assigns value of first argument to LOCAL variable $name (not $name from main prog!)
  print "What's your password, $name? ";
  my $userpass = <STDIN>;   # Neither of these 'my' variables will be visible outside of subroutine
  chomp $userpass;    # Can't declare & chomp at the same time
  if ( $userpass ne '' &&
   $userpass eq $password_list{ $name } ) # Subroutine stills knows about %password_list, because it's a GLOBAL hash
  { return 1; }     # This is value returned to the subroutine call on successful match: "true"
  else
  { return 0; }     # This is value returned to the subroutine call on failed match: "false"
 }




Reading external files, advanced loops, processing data & aborting (open, close, while, split & die)
 #!/usr/local/bin/perl
 &buildPasswordList( 'secret_password.file' );   # Self-contained tasks should generally be put into their own subroutines
 print "Hello, PerlMongers!\n";
 print "And what's your name? ";
 $name = <STDIN>;
 chomp $name;
 if ( $name eq 'Bill' )
 { print "Cool name, come on in!\n"; }
 elsif ( $name eq '' )
 { print "Fine, don't tell me...\n"; exit; }
 else
 {
  if ( &passwordOK( $name ) )
  { print "Darth Vader lives...come on in!\n"; }
  else
  { print "I thought I felt the good in you, but...\n"; exit; }
 }
 print "Welcome to Norad.  Do you want to play a game?\n";
 exit;

 sub buildPasswordList
 {
  my $filename = shift;     # Accepts real file name to connect to
  my ($line, $name, $password);    # Define as local variables to reduce changes of "hammering" global variables
  open (FILE,"<$filename")    # Create "filehandle" to access file data; < = read-only, > = overwrite, >> = append
   or die "Can't find password file!\n";  # Program exits with this msg if any problems with open statement
  while ($line = <FILE>)     # Loop will execute for each line in FILE in sequence, assigning value to $line
  {
   chomp $line;     # Ditch that newline!
   ($name, $password) = split(/\t/,$line);  # Split contents of $line on every tab char, assigning values to vars in parens
   $password_list{ $name } = $password;  # Build name/value pair in %password_list; DON'T MAKE THIS A LOCAL VAR!
  }
  close FILE;      # Close the filehandle; done anyway at program end, but good programming style

  return;       # Return is optional; this one returns nothing
 }

 sub passwordOK
 {
  my $name = shift;
  print "What's your password, $name? ";
  my $userpass = <STDIN>;
  chomp $userpass;
  if ( $userpass ne '' &&
   $userpass eq $password_list{ $name } )
  { return 1; }
  else
  { return 0; }
 }




Writing external files, accepting command-line arguments, negation & using shell commands (print FILE, @ARGV, !, system)
 #!/usr/local/bin/perl
 $name = $ARGV[0];      # Assign first argument (zero-indexed) in list to $name
 $password = $ARGV[1];      # Assign second argument in list to $password
 ($name, $password) = @ARGV;     # Alternate way of doing the above
 $filename = 'secret_password.file';
 $temp_filename = $filename . ".tmp";    # Temp file to hold data during processing

 # Make sure values are populated
 while ( length( $name ) < 1 )     # Loop will execute until $name contains a value; won't execute if already populated
 {
  print "Enter username: ";
  $name = <STDIN>;
 }
 while ( length( $password ) < 1 )
 {
  print "Enter password: ";
  $password = <STDIN>;
 }

 chomp ($name);       # Remember those newlines!
 chomp ($password);      #

 open(TMP,">$temp_filename");     # Selecting overwrite ensures data from previous runs is properly deleted
 open(FILE,"$filename");      # If no control prefix (>,>>,<,etc.), open defaults to read-only
 while ($line = <FILE>)      # Iterate through the file line-by-line
 {
  # NOTE: this should be done with regular expressions
  #
  my($file_username, $file_password) = split(/\t/,$line); # Break out values
  if ( $file_username eq $name )
  {
   # Found old entry; overwrite it
   print TMP "$name $password\n";  # Insert filehandle name after print to direct output to it
   $found_old_entry = 1;    # Set flag indicating that name/password has been written to the file
  }
  else
  {
   # This is not the entry; don't overwrite
   print TMP $line;     # Print current line to TMP unchanged
  }
 }
 if ( !$found_old_entry )      # A ! before a variable or conditional is a "negation" operator (yes returns no, etc.)
 {
  # No previous entry was found for this user; write it fresh
  print TMP "$name $password\n";
 }

 close FILE;       # Close filehandles
 close TMP;       #

 # Move TMP onto original file to complete edit process
 system("mv $temp_filename $filename");    # 'system' generates a new shell & executes supplied command, discarding output

 exit;




Recommended beginner reading:
1) Learning Perl, Randal L. Schwartz (O'Reilly)
 - the classic "starter" book, now in its 2nd edition
2) Perl Cookbook, Tom Christianson & Nat Torkington (O'Reilly)
 - hundreds of well-commented solutions to common problems, organized by subject: strings to OOP to web automation

Return to the Grand Rapids Perlmongers homepage.