Hello world
$ perl -e 'print "Hello world\n"'
Hello world
$ cat hello.pl
#!/usr/bin/perl
print "Hello world\n";
$ perl hello.pl
Hello world
$chmod a+x hello.pl
$./hello.pl
Hello world
Variables (scalar)
$apples = 3; $oranges = '4 oranges'; print $apples + $oranges + plums + $pears, "\n";
7
Conversion of types and usage of uninitialized variables and functions works but you should not take advantage of them except for one-liners.
When we add use strict;
to our code, we will get:
Global symbol "$apples" requires explicit package name at - line 2. Global symbol "$oranges" requires explicit package name at - line 2. Global symbol "$apples" requires explicit package name at - line 3. Global symbol "$oranges" requires explicit package name at - line 3. Global symbol "$pears" requires explicit package name at - line 3. Bareword "plums" not allowed while "strict subs" in use at - line 3. Execution of - aborted due to compilation errors.
Local variables declared
use strict; my ($apples, $oranges) = (3, '4 oranges'); my $pears; my $plums = 0; my $total = $apples + $oranges + $plums + $pears; print "Total: $total\n";
7
Even if all variables are declared as scoped local, we will get
warnings in runtime when we use warnings;
which we
probably should for anything but the smallest programs.
Argument "4 oranges" isn't numeric in addition (+) at - line 6. Use of uninitialized value $pears in addition (+) at - line 6. Total: 7
Strict hello world
The first minimal Hello world program should really read:
#!/usr/bin/perl use strict; use warnings FATAL => 'all'; print "Hello world\n"; 1; =head1 NAME Hello - greetings to the world. =head1 SYNOPSIS hello =head1 DESCRIPTION
Numeric vs. string operations
my $total = $apples + $oranges + $plums + $pears; print "Total: $total\n";
7
my $total = $apples . $oranges . $plums . $pears; print "Total: $total\n";
34 oranges
Reading input
my $i = 1; while (defined(my $line = <>)) { chomp $line; print "$i: [", $line, "]\n"; $i++; }
$ ./numit.pl /etc/passwd /etc/group
1: [root:x:0:0:root:/root:/bin/bash]
2: [bin:x:1:1:bin:/bin:/sbin/nologin]
3: [daemon:x:2:2:daemon:/sbin:/sbin/nologin]
...
113: [kvm:x:36:qemu]
114: [qemu:x:107:]
115: [ldap:x:55:]
$_
my $i = 1; while (<>) { chomp; print "$i: [$_]\n"; $i++; }
Other special global variables:
while (<>) { chomp $_; print "$.: [$_] in [$ARGV]\n"; }
1: [root:x:0:0:root:/root:/bin/bash] in [/etc/passwd] 2: [bin:x:1:1:bin:/bin:/sbin/nologin] in [/etc/passwd] ... 114: [qemu:x:107:] in [/etc/group] 115: [ldap:x:55:] in [/etc/group]
Looping from command line
while (<>) { print uc $_; }
$ perl -lne 'print uc' /etc/passwd
ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH
...
$ perl -MO=Deparse -Wlne 'print uc'
BEGIN { $^W = 1; }
BEGIN { $/ = "\n"; $\ = "\n"; }
use warnings;
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
print uc $_;
}
-e syntax OK
Regular expressions
while (<>) { print if /^root:/; }
while (defined(my $line = <>)) { print $line if $line =~ /^root:/; }
10.23.89.12 - - [18/Oct/2011:16:53:53 +0200] "GET / HTTP/1.1" 302 274 ⏎ "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.23)"
while (<>) { /^(\S+).*?"GET (\/\S*)/ and print "$1 $2\n"; }
# Database name, or more precisely database service name. db_name = prod # Database user. db_user = projectx # Password for the database user. db_password =
my $desc = ''; while (<>) { if (/^$/) { $desc = ""; next; } if (s/^#\s*//) { $desc .= $_; next } if (/^(\S+?)\s*=\s*(.*)/) { print "<dt>$1</dt>\n<dd>$desc</dd>\n"; print qq!<dd>The default value is "$2"</dd>\n!; $desc = ''; } else { warn "Unsupported config line $_" } }
One-liners
$ perl -ne 'if (/^$/) { $desc = "" } elsif (s/^#\s*//) { $desc .= $_ }⏎
elsif (/^(\S+?)\s*=/) { print "<dt>$1</dt>\n<dd>$desc</dd>\n" }'
# perl -i -pe 's!:/bin/false$!:/sbin/nologin!' /etc/passwd
$ perl -le 'print for 1001..1010'
Explicit open of files
local *FILE; open FILE, '<', $name or die "Error reading $name: $!\n"; while (<FILE>) { ... }
use IO::File (); my $fh = new IO::File $name, 'r' or die "Error reading $name: $!\n"; local *OUT; open OUT, '>', "$name.out" or die; while (defined(my $line = <$fh>)) { # process $line print OUT $line; }
while (defined(my $line = $fh->getline)) { ... }
Arrays
my $total = 0; for (@ARGV) { $total += $_; } print $total, "\n";
$ ./sum 79234 9923 0 12 -234
88935
my $total = 0; $total += $_ for @ARGV;
my $silent = 0; if (@ARGV and $ARGV[0] eq '-s') { $silent = 1; shift @ARGV; }
List context
print <$fh>;
my $i = 0; print map { ++$i . ': ' . $_ } <$fh>;
$perl -le 'print localtime'
11531518911122901 $perl -le 'print join " ", localtime'
11 53 15 18 9 111 2 290 1 $perl -le 'print scalar localtime'
Tue Oct 18 15:53:11 2011
my @time = localtime; printf "%02d:%02d:%02d\n", $time[2], $time[1], $time[0];
16:00:45
printf "%02d:%02d:%02d\n", (localtime)[2, 1, 0];
my ($name, $pass, $uid) = split /:/, $_;
my ($name, $uid) = (split /:/)[0, 2];
my ($name, $uid) = /^(.+?):.*?:(.+?):/;
Hashes (associative arrays)
$ /usr/bin/which ls httpd
/bin/ls
/usr/sbin/httpd
my @path = split /:/, $ENV{PATH}; for my $a (@ARGV) { for my $p (@path) { if (-f "$p/$a") { print "$p/$a\n"; last; } } }
$ ./which.pl ls httpd
/bin/ls
/usr/sbin/httpd
local *CONF; open CONF, '<', '/etc/projectx.conf' or die; my %config; while (<CONF>) { next if /^#/; if (/^\s*(\S+?)\s*=\s*(.+)/) { $config{$1} = $2; } } close CONF; use Data::Dumper; print Dumper \%config;
Functions
sub add { my $total = 0; for (@_) { $total += $_; } return $total; } my $soucet = add(3, 5, 12); my $soucet1 = add 3, 5, 12;
sub process { my ($dbh, $message) = @_; # ... $message =~ s/line/Line/; # ... }
my %config; sub parse_config_file { my $filename = shift; local *CONF; open CONF, '<', $filename or die "Error reading $filename: $!\n"; while (<CONF>) { next if /^#|^\s*$/; if (/^\s*(\S+?)\s*=\s*(.*)/) { $config{$1} = $2; } elsif (/^\s*include\s+(.+)/) { parse_config_file($1); } else { chomp; warn "Wrong syntax [$_] at $filename:$.\n"; } } close CONF; } parse_config_file($ENV{'PROJECTX_CONF'} // '/etc/projectx.conf');
References
sub parse_config_file { my $config = ( ref $_[0] ? shift : {} ); my $filename = shift; local *CONF; open CONF, '<', $filename or die "Error reading $filename: $!\n"; while (<CONF>) { next if /^#|^\s*$/; if (/^\s*(\S+?)\s*=\s*(.*)/) { $config->{$1} = $2; } elsif (/^\s*include\s+(.+)/) { parse_config_file($config, $1); } else { chomp; warn "Wrong syntax [$_] at $filename:$.\n"; } } close CONF; return $config; } my $config = parse_config_file('/etc/projectx.conf');
Using objects
use File::Temp (); my $tempfile = new File::Temp( 'DIR' => '/tmp', 'TEMPLATE' => 'projectxXXXX', ); my $filename = $tempfile->filename; print "Filename: ", $filename, "\n"; print $tempfile "Test\n"; $tempfile->flush(); print "Length: @{[ -s $filename ]}\n"; $tempfile->close; system "cat $filename"; $tempfile = undef; if (not open FILE, $filename) { print "File was removed\n"; }
Input data
$cat input8
maličký ježek $od -tx1 input8
0000000 6d 61 6c 69 c4 8d 6b c3 bd 20 6a 65 c5 be 65 6b 0000020 0a 0000021 $perl -ne 'print length, "\n"' input8
17 $perl -lpe 's/\W/./g' input8
mali..k...je..ek $perl -ne 'print uc' input8
MALIčKý JEžEK
$iconv --from-code UTF-8 --to-code ISO-8859-2 input8 > input2
$od -tx1 input2
0000000 6d 61 6c 69 e8 6b fd 20 6a 65 be 65 6b 0a 0000016 $perl -lpe 's/\W/./g' input2
mali.k..je.ek $perl -ne 'print uc' input2
MALI�K� JE�EK
Use characters internally
$perl -ne 'print uc' input8
MALIčKý JEžEK $perl -CSD -ne 'print uc' input8
MALIČKÝ JEŽEK
$perl -ne 'print uc' input2
MALI�K� JE�EK $perl -ne 'print uc' input2 | iconv -f ISO-8859-2 -t UTF-8
MALIčKý JEžEK $perl -e 'print uc <>' input2 | iconv -f ISO-8859-2 -t UTF-8
MALIčKý JEžEK $LC_CTYPE=cs_CZ perl -e 'use open ":locale"; print uc <>' input2 \ | iconv -f ISO-8859-2 -t UTF-8
MALIČKÝ JEŽEK $LC_CTYPE=cs_CZ perl -Mopen=:locale -e 'print uc <>' input2 \ | iconv -f ISO-8859-2 -t UTF-8
MALIČKÝ JEŽEK $LC_CTYPE=cs_CZ perl -CSD -Mopen=IN,:locale -e 'print uc <>' input2
MALIČKÝ JEŽEK
Specify encoding per filehandle
use open ':std', ':utf8'; local *FILE2; open FILE2, '<:encoding(iso-8859-2)', 'input2' or die; while (<FILE2>) { print uc; } close FILE2;
local *FILE2; open FILE2, '<', 'input2' or die; binmode FILE2, ':encoding(iso-8859-2)'; binmode STDOUT, ':utf8'; while (<FILE2>) { print uc; } close FILE2;
Change encoding mid-write
use warnings; use open ':std', ':utf8'; while (<>) { chomp; binmode STDOUT, ':utf8'; print "UTF-8: $_; "; binmode STDOUT, ':encoding(utf7)'; print "UTF-7: $_\n"; }
$ ./utf8utf7 input8
UTF-8: maličký ježek; UTF-7: mali+AQ0-k+AP0- je+AX4-ek
UTF-8 in source
use utf8; my $var = "maličký ježek"; print length($var), ", ", $var, "\n";
$ ./printfutf8
Wide character in print at - line 3.
13, maličký ježek
$ perl -CS ./printfutf8
13, maličký ježek
Or use open ":utf8", ":std"
.
Conversions
use Encode; use utf8; open FILE, '<:bytes', 'input2'; binmode STDOUT; while (<FILE>) { my $u = "Výstup: " . Encode::decode('iso-8859-2', $_); print Encode::encode('windows-1250', $u); }
use Encode; use utf8; open FILE, '<:bytes', 'input2'; binmode STDOUT; while (<FILE>) { my $u = "Výstup: " . Encode::decode('iso-8859-2', $_); while ($u ne '') { print Encode::encode('iso-8859-1', $u, Encode::FB_QUIET); $u =~ s/^./-/; } }
Self-assessment
Return-path: <bounce-user-23492fw90wweuw49@gmail.com> Envelope-to: peter@example.com Delivery-date: Thu, 20 Oct 2011 08:53:51 +0000 Date: Thu, 20 Oct 2011 08:53:51 +0000 From: John User <user@gmail.com> To: peter@example.com Subject: =?utf-8?B?UMWZw6Fuw60=?= Message-ID: <20111020085350.GB7116@relay4.gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit User-Agent: Web-based-super-mail/1.5.20 (2009-12-10) Všechno nejlepší k narozeninám. Honza
Write a Perl script which will read email message (see previous example) from standard input and print text representation of the email to standard output, suitable for example for SMS notifications. Use UTF-8 as the output character set.
Example output:
F: user@gmail.com S: Přání Všechno nejlepší k narozeninám. Honza
Also support different input charsets. Also support multipart emails (show content of text/plain part). Also support HTML-only emails (show text representation of those).
Make it perfect.
Final questions?
No?
OKay. Thank you!