Original slides from Dr. James Walden at Northern Kentucky University.
Source: https://wikipedia.org/wiki/Perl
Source: https://wikipedia.org/wiki/Perl
Source: https://wikipedia.org/wiki/Common_Gateway_Interface
#! /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.
#! /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.
#! /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
#! /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
#! /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.
#! /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
#! /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!
$
: indicates a scalar
@
: indicates an array
%
: indicates a hash, or associative array
#! /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
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
#! /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
#! /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
#! /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
#! /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.
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
#! /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.
#! /usr/bin/perl -w use 5.16.1; my $x = 42; say "Good!" if $x > 19;
Good!
Note the lack of parens and braces.
#! /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
is equivalent to print stderr
.
warn "This message goes to standard error.\n";
This message goes to standard error.
die
is darn handy—it’s like warn
+ exit
.
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.
#! /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
#! /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
#! /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
# 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
open(FILE, ">temp.txt"); print FILE "This line goes to the file.\n"; close(FILE);
<FOO>
returns:
FOO
, in a scalar context
FOO
, in a list context
<>
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; }
#! /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
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
#! /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
#! /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
#! /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
#! /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
#! /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
#! /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
There are several ways to run external programs:
$
out = `command`;
$
out
.
$all = `command 2>&1`;
$all
.
$status = system("command");
$status
(zero for success, non-zero for failure).
#! /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;
Fri Sep 6 07:14:56 MDT 2024 search cs.colostate edu colostate.edu nameserver 129.82.45.181 beethoven The exit code was 0
$ 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
$ 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
.
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]
@ARGV
contains command-line arguments.
argv
, $ARGV[0]
does not contain the program name,
it contains the first argument.
$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.
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
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
=~
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'
#! /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."; } }
bear has at least two vowels. bear has at least two vowels. beaver has at least two vowels. dog has less than two vowels. beaver has at least two vowels. bear has at least two vowels. beaver has at least two vowels. snake has at least two vowels. dog has less than two vowels.
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.
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
takes two arguments:
my @greek = ('alpha', 'beta', 'gamma', 'delta'); my @e = grep {/e/} @greek; print "@e";
beta delta
{
predicate code }
It calls the predicate code once per list item,
with $_
as the item, returning either:
How is it determined which is returned?
#! /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
.
: 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
booger
will become b**g*r
.
#! /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.
#! /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 …
#! /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
?
Source: https://www.cpan.org/modules/