Show Lecture.Perl as a slide show.
CT320 Perl
Original slides from Dr. James Walden at Northern Kentucky University.
Language
- Perl is a high-level, general-purpose, interpreted programming
language developed for Unix scripting.
- Practical Extraction and Reporting Language
developed by a Unisys engineer (Larry Wall) for report processing.
- Borrows features from C, shell scripting, awk, and sed,
including powerful text processing facilities.
- Popular for web server, graphics, system admin, and network
programs, among other areas.
Source: https://wikipedia.org/wiki/Perl
History
- Perl 1 released in 1987 via the comp.sources.misc newsgroup,
then expanded rapidly.
- Perl 2 released in 1988, with improved regular expressions,
Perl 3 in 1989 added binary data streams.
- Originally documented by a single manpage, when the Camel
Book came out version was bumped to Perl 4.
- Perl 5.0 released in 1994, since then many incremental
releases, version on CSU machines is 5.28.2 as of October 2019.
- Perl 6 is available for Ubuntu, if you install the Rakudo compiler.
Source: https://wikipedia.org/wiki/Perl
Usage (continued)
- Web site and web application development
- Internet Movie Database (IMDB)
- Test frameworks for web and desktop applications
- Configuration management, build, packaging
- System administration, periodic tasks
- Common Gateway Interface (CGI)
- Bioinformatics, Databases, Graphics, etc.
Usage (continued)
- Common Gateway Interface (CGI):
- Method for web server software to delegate the generation of web
content to executable files.
- A web server that supports CGI can be configured to interpret a
URL that it serves as a reference to a CGI script.
- In the case of HTTP PUT or POSTs, the user-submitted data is
provided to the program via the standard input.
- The program generates content, writes it to its standard output,
and the web server transmits it to the browser.
Source: https://wikipedia.org/wiki/Common_Gateway_Interface
Documentation
Basic Perl
#! /usr/bin/perl -w
use 5.16.1;
say "Hello, world!"
Hello, world!
#! /usr/bin/perl
means that this is a Perl script. Always do this.
-w
means “enable warnings”. Always do this.
use 5.16.1;
requires at least that version, and enables new
features, such as say
. Always do this.
use 5.99.9;
Perl v5.99.9 required--this is only v5.26.3, stopped at .perl-code line 1.
BEGIN failed--compilation aborted at .perl-code line 1.
print vs. say
#! /usr/bin/perl -w
use 5.16.1;
print "a",'b',2+2;
say "d";
say "e";
say "f";
ab4d
e
f
print
sends all arguments to standard output.
say
does the same, then adds a newline.
Scalars
#! /usr/bin/perl -w
use 5.16.1;
my $alpha = 123456;
my $beta = 3.14159;
my $gamma = "String";
say '$alpha = ', $alpha;
say '$beta = ' . $beta;
say "\$gamma = $gamma";
$alpha = 123456
$beta = 3.14159
$gamma = String
- Perl has scalar values that can be integer, float, or string.
- Perl does not have strong data typing like Java or C++.
- Can determine type with a regular expression.
- Also there are loadable packages with data type queries.
Quotes
#! /usr/bin/perl -w
use 5.16.1;
my $answer = 42;
print "1: answer is ", $answer, "\n";
print "2: answer is $answer\n";
print '3: answer is $answer\n';
1: answer is 42
2: answer is 42
3: answer is $answer\n
$
variable doesn’t work inside single quotes
\n
doesn’t work inside single quotes
Math
#! /usr/bin/perl -w
use 5.16.1;
say 1 + 2;
say 3 - 4;
say 5 * 6;
say 7 / 8;
say 3 ** 4;
say 13 % 2;
3
-1
30
0.875
81
1
Unlike some languages, int/int performs floating-point division.
Strings
#! /usr/bin/perl -w
use 5.16.1;
my $alpha = "Foo";
my $beta = 'Bar';
my $gamma= $alpha . $beta;
my $delta = 3;
my $epsilon = $beta x $delta;
say "alpha = $alpha";
say "beta = $beta";
say "gamma = $gamma";
say "delta = $delta";
say "epsilon = $epsilon";
alpha = Foo
beta = Bar
gamma = FooBar
delta = 3
epsilon = BarBarBar
Declarations
#! /usr/bin/perl -w
use 5.16.1;
my $alpha = 42;
print "alpha is $a1pha";
Global symbol "$a1pha" requires explicit package name (did you forget to declare "my $a1pha"?) at .perl-code line 4.
Execution of .perl-code aborted due to compilation errors.
The last line contained a digit one, as opposed to a lower-case L.
Oops!
Sigils
- Three basic sigils (soft “g”, like “pigeon”):
$
: indicates a scalar
@
: indicates an array
%
: indicates a hash, or associative array
- The sigil indicates the type of the value,
not the type of the variable.
#! /usr/bin/perl -w
use 5.16.1;
my $a = 5; # a is a scalar
my @b = (11,22,33); # b is an array
$a = $b[1]; # b[1] is a scalar
say $a;
22
Perl ≠ Bash
Note the differences:
#! /bin/bash
let x=2+2
let x*=10
let y=$x+9
echo "x=$x y=$y"
x=40 y=49
#! /usr/bin/perl -w
use 5.16.3;
my $x=2+2;
$x*=10;
my $y=$x+9;
say "x=$x y=$y";
x=40 y=49
Arrays
#! /usr/bin/perl -w
use 5.16.1;
# Array Definition
my @stuff = ("apples", "pears", "eels");
my @numbers = (123, 456, 789, 987, 654, 321);
# Array access
say '$stuff[2] = ', $stuff[2];
say '$numbers[4] = ', $numbers[4];
$stuff[2] = eels
$numbers[4] = 654
Arrays (continued)
#! /usr/bin/perl -w
use 5.16.1;
# Array Definition
my @stuff = ("apples", "pears", "eels");
my @numbers = (123, 456, 789, 987, 654, 321);
# Adding and removing elements
push(@stuff, "eggs", "lard");
my @more = ("butter", "milk");
push(@stuff, @more);
print "stuff = @stuff\n";
my $grub = pop(@stuff);
print "grub = $grub\n";
print "stuff = @stuff\n";
stuff = apples pears eels eggs lard butter milk
grub = milk
stuff = apples pears eels eggs lard butter
Arrays (continued)
#! /usr/bin/perl -w
use 5.16.1;
# Length versus contents
my @a = (1,2,3,"cuatro","cinco","seis");
my $x = @a;
say "x = $x";
$x = "@a";
say "x = $x";
x = 6
x = 1 2 3 cuatro cinco seis
List manipulation
#! /usr/bin/perl -w
use 5.16.1;
my @a = (2,3,"cuatro","cinco");
push @a, "seis";
unshift @a, 1;
say "a: @a";
say "pop yields: ", pop(@a);
say "shift yields: ", shift(@a);
say "now, a: @a";
a: 1 2 3 cuatro cinco seis
pop yields: seis
shift yields: 1
now, a: 2 3 cuatro cinco
push
and pop
manipulate the end of the list, at the right.
unshift
and shift
manipulate the start of the list, at the left.
- The names push and pop are standard CS terms for a stack.
- shift comes from shell scripts.
- unshift is the opposite of shift.
Hashes
Hashes are also called associative arrays. They’re like regular
arrays, except that the subscripts, or indices, can be strings.
#! /usr/bin/perl -w
use 5.16.1;
my %phonebook = ("Jack" => 5551212, "Peter" => 8765329);
$phonebook{"George"} = 1234567;
say $phonebook{"Peter"};
say $phonebook{"George"};
if ($phonebook{"Jack"}) {
say "Jack’s phone number is ", $phonebook{"Jack"};
}
if ($phonebook{"Mo"}) {
say "Mo’s phone number is ", $phonebook{"Mo"};
}
8765329
1234567
Jack’s phone number is 5551212
if statements
#! /usr/bin/perl -w
use 5.16.1;
my $x = 42;
if ($x > 19) {
say "Good!";
}
Good!
The (
… )
and {
… }
are required.
While-loops are similar.
Postfix if
#! /usr/bin/perl -w
use 5.16.1;
my $x = 42;
say "Good!"
if $x > 19;
Good!
Note the lack of parens and braces.
Reverse if
#! /usr/bin/perl -w
use 5.16.1;
my $x = 42;
say "Good!"
unless $x == 99;
Good!
You can also use unless
in a non-reversed if
.
warn
warn
is equivalent to print stderr
.
warn "This message goes to standard error.\n";
This message goes to standard error.
die
die
is darn handy—it’s like warn
+ exit
.
- It terminates the program, with an optional error message
sent to standard error.
- In addition, if the error message does not end with a newline,
the the source file and line number;
- The first thing that I do in nearly all programming languages is
emulate Perl’s
die
.
if (3/2 > 1) {
die "I didn’t expect that!";
}
I didn’t expect that! at .perl-code line 2.
open F, "</bogus/noway" or die;
Died at .perl-code line 1.
File I/O
#! /usr/bin/perl -w
use 5.16.1;
# File Input
open(NETS, "/etc/networks") or die "$0: can’t open /etc/networks";
while (<NETS>) {
print $_;
}
close(NETS);
loopback 127 loopback-net,localnet
colostate 129.82
cs.colostate 129.82.44
File I/O, take two
#! /usr/bin/perl -w
use 5.16.1;
# File Input
open(NETS, "/etc/networks") or die "$0: can’t open /etc/networks";
my @lines = <NETS>;
close(NETS);
print @lines;
loopback 127 loopback-net,localnet
colostate 129.82
cs.colostate 129.82.44
File I/O, take three
#! /usr/bin/perl -w
use 5.16.1;
# File Input
open(NETS, "/etc/networks") or die "$0: can’t open /etc/networks";
print <NETS>;
close(NETS);
loopback 127 loopback-net,localnet
colostate 129.82
cs.colostate 129.82.44
- C programmers manipulate arrays element-by-element
- Perl programmers manipulate arrays as single entities
File I/O
# File modes
open(INFO, "datafile"); # Open for input
open(INFO, ">datafile"); # Open for output
open(INFO, ">>datafile"); # Open for append
open(INFO, "<datafile"); # Open for input
File Output
open(FILE, ">temp.txt");
print FILE "This line goes to the file.\n";
close(FILE);
Diamond Operator
- The diamond operator
<FOO>
returns:
- a single line from the file handle
FOO
, in a scalar context
- a list of all lines the file handle
FOO
, in a list context
- The special operator
<>
is like <FOO>
, except that it
implicitly reads from @ARGV
. If @ARGV
is empty, then
it reads from standard input. It does the “right thing”.
#! /usr/bin/perl -w
# This program imitates the cat command.
use 5.16.1;
while (<>) {
print;
}
Loops
#! /usr/bin/perl -w
use 5.16.1;
my $i = 0;
do {
say "do while $i";
} while $i++ < 3;
my $j = 3;
while ($j-- > 0) {
say "while $j";
}
for my $w (4..6) {
say "for $w";
}
for (my $z=10; $z<20; $z+=3) {
say "z=$z";
}
do while 0
do while 1
do while 2
do while 3
while 2
while 1
while 0
for 4
for 5
for 6
z=10
z=13
z=16
z=19
Poetry
Many Perl functions can be called without parentheses:
#! /usr/bin/perl -w
use 5.16.1;
print("How are you?\n");
my @a = (11,22,33);
push(@a, 42, sqrt(10));
say(pop(@a));
say("a: @a");
How are you?
3.16227766016838
a: 11 22 33 42
#! /usr/bin/perl -w
use 5.16.1;
print "How are you?\n";
my @a = (11,22,33);
push @a, 42, sqrt 10;
say pop @a;
say "a: @a";
How are you?
3.16227766016838
a: 11 22 33 42
Functions
#! /usr/bin/perl -w
use 5.16.1;
# Add all the numbers given as arguments.
sub total {
my (@numbers) = @_; # Copy arguments
my $sum = 0;
foreach my $item (@numbers) {
$sum += $item;
}
return $sum;
}
# Main program
my @data1 = (3,1,4,1,5,9);
my $answer = total(@data1);
say "Sum of @data1 is $answer";
say "Sum of (1..15) is ", total(1..15);
Sum of 3 1 4 1 5 9 is 23
Sum of (1..15) is 120
Factorial, take 1
#! /usr/bin/perl -w
use 5.16.1;
# Return the product of 1…the given argument.
sub factorial {
my ($n) = @_;
if ($n <= 1) {
return $n;
}
else {
return $n*factorial($n-1);
}
}
say "5! = ", factorial(5); # 5! = 5×4×3×2×1 = 120
say "69! = ", factorial(69);
5! = 120
69! = 1.71122452428141e+98
Factorial, take 2
#! /usr/bin/perl -w
use 5.16.1;
# Return the product of 1…the given argument.
sub factorial {
my ($n) = @_;
if ($n <= 1) {
return $n;
}
return $n*factorial($n-1);
}
say "5! = ", factorial(5); # 5! = 5×4×3×2×1 = 120
say "69! = ", factorial(69);
5! = 120
69! = 1.71122452428141e+98
Factorial, take 3
#! /usr/bin/perl -w
use 5.16.1;
# Return the product of 1…the given argument.
sub factorial {
my ($n) = @_;
return $n if $n <= 1;
return $n*factorial($n-1);
}
say "5! = ", factorial(5); # 5! = 5×4×3×2×1 = 120
say "69! = ", factorial(69);
5! = 120
69! = 1.71122452428141e+98
Factorial, take 4
#! /usr/bin/perl -w
use 5.16.1;
# Return the product of 1…the given argument.
sub factorial {
my ($n) = @_;
return $n <= 1 ? $n : $n*factorial($n-1);
}
say "5! = ", factorial(5); # 5! = 5×4×3×2×1 = 120
say "69! = ", factorial(69);
5! = 120
69! = 1.71122452428141e+98
Factorial, take 5
#! /usr/bin/perl -w
use 5.16.1;
# Return the product of 1…the given argument.
sub factorial {
my ($n) = @_;
$n <= 1 ? $n : $n*factorial($n-1);
}
say "5! = ", factorial(5); # 5! = 5×4×3×2×1 = 120
say "69! = ", factorial(69);
5! = 120
69! = 1.71122452428141e+98
External Programs
There are several ways to run external programs:
-
$
out = `command`;
-
(Those are `backquotes`, not normal 'single quotes'.)
Execute the Linux command.
The standard output of the command goes into $
out
.
-
$all = `command 2>&1`;
-
Executes the Linux command.
The standard output and standard error of the command
go into
$all
.
-
$status = system("command");
-
Execute the Linux command.
The standard output and standard error are not captured at all,
and just merge with the Perl program’s output.
The exit code is captured in
$status
(zero for success, non-zero for failure).
External Command Examples
#! /usr/bin/perl -w
use 5.16.1;
my $a = `date`; # Note backquotes
print $a; # Includes a newline
my @info = `head -4 /etc/resolv.conf`;
print $info[0]; # first line
print $info[1]; # second line
my $status = system('cat /etc/hostname');
say 'The exit code was ', $status;
Sat Nov 23 05:38:05 MST 2024
search cs.colostate edu colostate.edu
nameserver 129.82.45.181
beethoven
The exit code was 0
Admin Example
$ cat /etc/resolv.conf
search cs.colostate edu colostate.edu
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79
#! /usr/bin/perl -w
use 5.16.1;
my $path = "/etc/resolv.conf";
open (CONFIG, $path);
say "Nameservers from $path:";
while (<CONFIG>) {
chomp;
my @z = split(' ', $_);
say $z[1]
if lc($z[0]) eq "nameserver";
}
Nameservers from /etc/resolv.conf:
129.82.45.181
129.82.103.78
129.82.103.79
Admin Example, take two
$ cat /etc/resolv.conf
search cs.colostate edu colostate.edu
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79
#! /usr/bin/perl -w
use 5.16.1;
my $path = "/etc/resolv.conf";
open (CONFIG, $path);
say "Nameservers from $path:";
while (<CONFIG>) {
say $1
if /^nameserver\s+(.+)$/i;
}
Nameservers from /etc/resolv.conf:
129.82.45.181
129.82.103.78
129.82.103.79
$_
$_
is the “default” variable. For many Perl actions,
if you don’t give a varable, $_
steps up.
#! /usr/bin/perl -w
use 5.16.1;
for my $i (5..9) {
say $i;
}
5
6
7
8
9
#! /usr/bin/perl -w
use 5.16.1;
for (5..9) {
say $_;
}
5
6
7
8
9
#! /usr/bin/perl -w
use 5.16.1;
for (5..9) {
say;
}
5
6
7
8
9
#! /usr/bin/perl -w
use 5.16.1;
say
for 5..9;
5
6
7
8
9
undef
- Some operations return the undefined value,
undef
.
- Test for
undef
using the defined()
predicate:
#! /usr/bin/perl -w
use 5.16.1;
my @a = ("alpha", 'beta', undef, "delta");
for my $i (0..5) {
if (defined($a[$i])) {
say "a[$i]: $a[$i]";
}
else {
say "There is no a[$i]";
}
}
a[0]: alpha
a[1]: beta
There is no a[2]
a[3]: delta
There is no a[4]
There is no a[5]
Arguments
- The global array
@ARGV
contains command-line arguments.
- Unlike C’s
argv
, $ARGV[0]
does not contain the program name,
it contains the first argument.
- The program name is in
$0
.
#! /usr/bin/perl -w
use 5.16.1;
say "This program is $0.";
# Display all arguments:
for (my $i=0; $i<@ARGV; $i++) {
say "arg $i: $ARGV[$i]";
}
This program is .perl-code.
Context
Consider this C program. Why doesn’t it display one-third?
#include <stdio.h>
int main() {
double d = 1/3;
printf("%f\n", d);
return 0;
}
0.000000
Context matters
In Perl, however, context matters:
#! /usr/bin/perl -w
use 5.16.1;
my @alpha = ("Every", "good", "boy", "does", "fine");
my @beta = @alpha;
say "beta is @beta";
my $gamma = @alpha;
say "gamma is $gamma";
beta is Every good boy does fine
gamma is 5
Pattern matching
- Regular expressions are big in Perl.
- The
=~
operator takes a string and a regular expression
(like grep
) to match against.
#! /usr/bin/perl -w
use 5.16.1;
my $a = "Constantinople";
say "$a ends with an 'e'"
if $a =~ /e$/;
say "$a starts with a digit"
if $a =~ /^\d/;
Constantinople ends with an 'e'
More pattern matching
#! /usr/bin/perl -w
use 5.16.1;
my @animals = ("dog", "fish", "bear", "snake", "beaver");
for (1..9) {
my $a = $animals[rand(@animals)];
if ($a =~ /[aeiouy].*[aeiouy]/) {
say "$a has at least two vowels.";
}
else {
say "$a has less than two vowels.";
}
}
beaver has at least two vowels.
bear has at least two vowels.
bear has at least two vowels.
snake has at least two vowels.
dog has less than two vowels.
bear has at least two vowels.
snake has at least two vowels.
snake has at least two vowels.
dog has less than two vowels.
Implicit pattern matching
If the =~
operator is not present, then the pattern matches against
the special variable $_
.
#! /usr/bin/perl -w
use 5.16.1;
open INFO, "/proc/cpuinfo" or die;
my $num_procs=0;
while (<INFO>) {
$num_procs++ if /^processor/;
}
say "This computer has $num_procs processors.";
This computer has 12 processors.
Better implicit pattern matching
Rather than iterate explicitly, let’s use grep
:
#! /usr/bin/perl -w
use 5.16.1;
open INFO, "/proc/cpuinfo" or die;
my $num_procs = grep {/^processor/} <INFO>;
say "This computer has $num_procs processors.";
This computer has 12 processors.
grep
grep
takes two arguments:
my @greek = ('alpha', 'beta', 'gamma', 'delta');
my @e = grep {/e/} @greek;
print "@e";
beta delta
{
predicate code }
- A list to process
It calls the predicate code once per list item,
with $_
as the item, returning either:
- the elements that matched (the predicate returned true)
- a count of the matching elements.
How is it determined which is returned?
More Implicit pattern matching
#! /usr/bin/perl -w
use 5.16.1;
open M, "/proc/mounts" or die;
my @filesystems;
while (<M>) {
push @filesystems, "$1 on $2\n"
if /^(\/dev\/\S+) (\S+)/;
}
print sort(@filesystems);
/dev/md1 on /s/beethoven/b
/dev/sda1 on /boot/efi
/dev/sda3 on /
/dev/sda3 on /var/tmp
/dev/sda4 on /s/beethoven/a
Some regexp syntax
- Common regular expression syntax:
.
: any character
?
: zero or one of what came before
*
: zero or more of what came before
+
: one or more of what came before
[abcf-j]
: any one of those characters
alpha|beta
: either one
(pattern)
: grouping, copy it to $1
or $2
…
^
, $
: start of line, end of line
\d
, \D
: digits, non-digits
\s
, \S
: spaces, non-spaces
\w
, \W
: alphanumerics (and _), non-alphanumerics
Changing text: problem statement
- I want to write Perl code to display the computer’s name.
- However, to avoid offending management, let’s replace all the
vowels with asterisks.
booger
will become b**g*r
.
- How do we accomplish this?
Changing text, pass 1
#! /usr/bin/perl -w
use 5.16.1;
open H, "/etc/hostname" or die; # Quite casual error message!
my $name = <H>;
print "Original hostname: $name";
for (my $i=0; $i<length($name); $i++) {
my $l = substr($name, $i, 1);
if ($l eq 'a' || $l eq 'e' || $l eq 'i' || $l eq 'o' || $l eq 'u') {
substr($name, $i, 1) = '*';
}
}
print "New hostname: $name";
Original hostname: beethoven
New hostname: b**th*v*n
This is how a C programmer would do it.
Changing text, pass 2
#! /usr/bin/perl -w
use 5.16.1;
open H, "/etc/hostname" or die;
my $name = <H>;
print "Original hostname: $name";
for (my $i=0; $i<length($name); $i++) {
my $l = substr($name, $i, 1);
if ($l =~ /[aeiou]/) {
substr($name, $i, 1) = '*';
}
}
print "New hostname: $name";
Original hostname: beethoven
New hostname: b**th*v*n
Better …
Changing text, pass 3
#! /usr/bin/perl -w
use 5.16.1;
open H, "/etc/hostname" or die;
my $name = <H>;
print "Original hostname: $name";
$name =~ s/[aeiou]/*/g; # sed syntax
print "New hostname: $name";
Original hostname: beethoven
New hostname: b**th*v*n
This is how a Perl programmer would do it.
Why didn’t we need to use say
or add a \n
?
Packages
- Perl offers a set of modules, just as Java has packages, that
contain domain-specific functionality:
- Security and Encryption
- Internationalization and Locale
- Database Interfaces
- User Interfaces
- Data Type Utilities
- Archiving and Compresion
- etc., etc., etc.
Source: https://www.cpan.org/modules/
Features
- Other Perl language features:
- Arrays: merge, append, sort
- Hashing: create, append, print
- Parameter Passing
- Operator Precedence
- Hexadecimal and Binary Conversion
- Creating Packages
- Database Connectivity
- Object Oriented Perl
Source: https://linuxconfig.org/perl-programming-tutorial/