CT320

CT320: Network and System Administration

Fall 2018

Perl

See this page as a slide show

Perl Programming

Colorado State University

Computer Science Department

CT 320: Network and System Administration

Original slides from Dr. James Walden at Northern Kentucky University.

Language

Source: https://wikipedia.org/wiki/Perl

History

Source: https://wikipedia.org/wiki/Perl

Usage (continued)

Usage (continued)

Source: https://wikipedia.org/wiki/Common_Gateway_Interface

Documentation

Basic Perl

#! /usr/bin/perl -w
use 5.16.1;
say "Hello, world!"
Hello, world!

print vs. say

#! /usr/bin/perl -w
use 5.16.1;
print "a";
print "b";
print "c";
say "d";
say "e";
say "f";
abcd
e
f

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

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

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 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

#! /usr/bin/perl -w
use 5.16.1;
my $alpha = 5;              # alpha is a scalar
my @beta = (11,22,33);      # beta is an array
$alpha = $beta[1];          # beta[1] is a scalar, however
say $alpha;
22

It’s the type of the value, not the type of the variable.

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

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

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

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

#! /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

System Calls

#! /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;
Wed Jan 24 00:44:18 MST 2018
search cs.colostate.edu colostate.edu
nameserver 129.82.45.181
parsons
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+(.+)$/;
}
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

#! /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

#! /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

#! /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.";
    }
}
fish has less than two vowels.
fish has less than two vowels.
dog has less than two vowels.
dog has less than two vowels.
dog has less than two vowels.
snake has at least two vowels.
fish has less than two vowels.
beaver has at least two vowels.
fish 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 20 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 20 processors.

grep

grep takes two arguments:

my @greek = ('alpha', 'beta', 'gamma', 'delta');
my @e = grep {/e/} @greek;
print "@e";
beta delta

It calls the predicate code once per list item, with $_ as the item, returning either:

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/sda1 on /boot/efi
/dev/sda3 on /
/dev/sda3 on /var/tmp
/dev/sda4 on /s/parsons/tmp
/dev/sdb1 on /s/parsons/a
/dev/sdc1 on /s/parsons/b
/dev/sdd1 on /s/parsons/c
/dev/sde1 on /s/parsons/d
/dev/sdf1 on /s/parsons/e
/dev/sdg1 on /s/parsons/f
/dev/sdh1 on /s/parsons/g
/dev/sdi1 on /s/parsons/h
/dev/sdj1 on /s/parsons/i
/dev/sdk1 on /s/parsons/j
/dev/sdl1 on /s/parsons/k
/dev/sdm1 on /s/parsons/l
/dev/sdn1 on /s/parsons/m
/dev/sdo1 on /s/parsons/n
/dev/sdp1 on /s/parsons/o
/dev/sdq1 on /s/parsons/p

Some regexp syntax

Changing text: problem statement

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: parsons
New hostname: p*rs*ns

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: parsons
New hostname: p*rs*ns

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: parsons
New hostname: p*rs*ns

This is how a Perl programmer would do it.

Why didn’t we need to use say or add a \n?

Packages

Source: https://www.cpan.org/modules/

Features

Source: https://linuxconfig.org/perl-programming-tutorial/

Modified: 2017-12-14T09:09

User: Guest

Check: HTML CSS
Edit History Source
Apply to CSU | Contact CSU | Disclaimer | Equal Opportunity
Colorado State University, Fort Collins, CO 80523 USA
© 2015 Colorado State University
CS Building