Perlkurset, Høsten '99

I regi av Programvareverkstedets Faglige Forum


Kursoversikt

Kurset er beregnet for de som er kjent med grunnleggende programmering inkludert funksjoner, bibliotek og elementære objekt-orienteringsmetoder. Vi vil se på språkets sterke og svake sider, de viktigste grunnelementene samt mer avanserte ting som komplekse datastrukturer og objekt-orientert programmering. Vi skal også lage enkle og mer avanserte perl-script, bruke moduler og dokumentere på en enkel måte. Perl under Microsoft-plattformer blir gledelig ignorert - UNIX er tingen! :^)



Del 1: Hva er Perl?

Liten

perl -e 'print "Hello, World!\n";'

Åpen

"Practical Extraction and Report Language" er et fjerdegenerasjons språk som er laget for "å få jobben gjort." Språket er over ti år gammelt, og uvikles som et OSS-prosjekt av Larry Wall i samarbeid med flere hundre mennesker fra hele verden. Perl og de aller fleste modulene er lisensiert under "GNU Public License" eller "Artistic License" - du velger selv.

Kraftig

Perl har flere GUI-moduler, støtter alle lavnivå internettprotokollene, har uhyre kraftige tekstbehandlings-muligheter, tillater både funksjonell og OO-programmering, har meget gode database-API, og tillater det meste noen skulle trenge å programmere i løpet av en dag.

'Naturlig'

Perl er fra begynnelsen av blitt utviklet som et "naturlig" språk - det vil si at det er så dynamisk at du kan gjøre nyttige ting selv om du bare kan noen få ord, eller at du kan bruke år på å mestre hele språket. Det er lett å lære seg nye elementer i språket, og det finnes altids en nåte å uttrykke seg på som passer din programmeringsstil.

Til fare for liv og helse!

Dessverre er det for lett å programmere dårlig eller farlig i perl - slikt kommer ofte med på kjøpet når man velger å bruke et kraftig verktøy. Perl gir nok muligheter til at du ikke bare kan skyte deg selv i foten, men faktisk gjøre det med stil, ekstravaganse og utrolig letthet! Heldigvis har språket et par hjelpemidler så du unngår de groveste dumhetene. Det gir derimot ikke grunn til å la være å passe på!

Litt større

Om du vil, kan du også bryne deg på litt "vanskeligere" ting. (Denne er egentlig ikke så vanskelig! Etter kurset bør du kunne finne ut hva som skjer.)

#!/usr/bin/perl
# En obfuskert signaturfil?
($_='$ZNVYGB+$FWA?$CII:$BET;')?y:;ZA-Y\:?+${':\nm-za-l.@\:\0:?print:'}.':'.$';



Del 2: Pathologically Eclectic Rubbish Lister

Hva egner perl seg til?

  • Vanlige og uvanlige systemadministrasjonsoppgaver.
  • Nettverkskommunikasjon.
  • CGI-programmering.
  • "Rapid Prototyping."
  • "Lim" mellom applikasjoner.
  • Effektiv behandling av store mengder tekst (HTML? XML? loggfiler?)

Hva egner det seg ikke til?

  • Svært store og/eller komplekse prosjekter.
  • Arbeidsoppgaver der maskinressursene er knappe.
  • Oppgaver det programmet krever svært rask utførelse og/eller høy responstid.

Men tenk over oppgaven!

Det er mulig perl ikke er den beste løsningen til akkurat din oppgave! Det finnes mange språk, og vårt egner seg ikke til alt (selv om det kan brukes til svært mye.) Om et skriptspråk er løsningen, ta gjerne også en titt på Python, TCL, Scheme eller til og med Visual Basic - alle disse har egne fordeler og ulemper. Noen ganger trenger du ytelsen fra lavnivåspråk, og da er det meget mulig at Perl er feil løsning.... Kanskje det smarteste er å lage en perl-modul i C? :^)



Del 3: Latskap, Utålmodighet og Hovmod!

Perl-programmererens tre dyder

  • Latskap, fordi du velger å skrive kode en gang, slik at det også kan brukes andre steder og av andre. Merk også at du dokumenterer skikkelig, for da slipper du å svare på dumme spørsmål fra folk som bruker koden din!
  • Utålmodighet, fordi du ikke vil vente på datamaskinen hele tiden, og heller passer på at koden din ikke bare reagerer på behovene dine, men faktisk forutser dem!
  • Hovmod, fordi det får deg til å skrive kode som andre ikke vil kritisere.
Følg dem, og alt går så meget bedre!



Del 4: Fordeler og Ulemper

Plusspoeng

  • Perl er et høynivå språk med tilgang på lavnivå kall om man trenger det.
  • Å programmere i perl går fort - man kan få mye effekt ut av få linjer kode, noe som man kan oversette direkte til spart tid og penger.
  • Perl har et rikt utvalg av moduler som omhandler alt fra kryptering og autentisering til verktøy som kan brukes i molekylærbiologi og til å samle e-postaadresser på USENET. (Ikke gjør det!)
  • Perl har flere innebygde sikkerhetsfunksjoner, og et godt utvalg med moduler som gjør livet lettere.
  • Det går an å "vokse" med språket, siden det er enkelt, kraftig og smidig. Det er alltid noe nytt du kan lære og et triks som gjør programmeringen morsommere. (Perl er en skrekk for språkminimalister.)

Minuspoeng

  • Perl tar - og beholder - alt den trenger av ressurser. Det er ikke vanskelig å sluke alle ressursene på en maskin om man ikke passer på!
  • Kildekoden i et perl-program kan lett gjøres uleselig! Prøv å søke etter "Obfuscated Perl contest" på nettet, og forbered deg på en stygg hodepine.
  • Språket kan for enkelte være tungt å lære/bruke på grunn av mengden med "spesialvariabler."
  • Perl er en skrekk for språkminimalister. (Det går an å "vokse" med språket, siden det er enkelt, kraftig og smidig. Det er alltid noe nytt du kan lære og et triks som gjør programmeringen morsommere.)
  • Språket lærer deg ikke å programmere godt/pent - det må du gjøre selv! Ikke bli fristet til å skrive "bruk og kast-kode".



Del 5: Datastrukturer i Perl

I perl opererer man i hovedsak med tre typer data (det finnes flere, men de er ikke så viktige i starten.)

  • "Scalar" - Et enkeltelement (tekst, tall, pekere, m.m.) - $foo.
  • "Array" - En liste av enkeltelementer, med tall fra null og oppover som indeksverdier - @bar, $bar[0] eller $bar[$tall].
  • "Hash" - En liste med navngitte indeksverdier i stedet for tall ("Hashes" ogr også kjent som "Assosiative Arrays") - %baz, $baz{tekst} eller $baz{$indeks}.
Variabler og datastrukturer trenger ikke å forhåndsdeklareres, men kan istedet opprettes når man trenger dem. Dette betyr ikke at det er dumt å deklarere dem på forånd! Faktisk, glem det jeg skrev om valgfri forhåndsdeklarering, og lat som om du må gjøre det!



Del 6: Scalars (skalare verdier?)

Skalare verdier angis ved å bruke en '$' foran variabelnavnet. $navn vil angi at den enten er en streng, et tall eller en referanse. I alle tilfeller vil variabelen kun inneholde en eneste verdi.

my $hilsen = 'heisann';
my $person = 'Per';
print "$hilsen, $person! Har du det bra?\n";
my( $svar, $tilstand ) = ( "joda", "bra" );
print "$svar, det går $tilstand.\n";

I mange programmeringsspråk (f.eks. C eller Pascal) deklarerer man typen på variabelen (f.eks. "char* hilsen;") før den brukes, noe som ikke er nødvendig i perl - et tall er en skalar, og ikke en integer, float, double eller slikt. Bare husk å forhåndsdeklarere variablene (selv om du ikke må.) Dette kan gjøres ved å bruke my() foran variabelen, eller ved å deklarere dem globalt med use vars ('$hilsen', '$person');. Om du vil konkatenere strenger, kan du bruke "." (punktum) mollom strengene.

my $tid = scalar localtime;  # hva gjør scalar() ?
print "Vi fanget dette øyeblikket: " . $tid;

En viktig poeng:

my $tekst = "foo";
print "Med doble fnutter: $tekst";
print 'Med enkle fnutter: $tekst';  # hva er forskjellen?



Del 7: Arrays

En liste av "skalare verdier" indeksert med tall, der første indeksverdi er null.

my @array = ( "a", "b", "c", "d" );
print $array[1];   # Skriver ut "b"
$array[2] = "foo";
print @array;      # Skriver ut "afoocd"
print "@array";	   # Med resultat "a foo c d"

Det er verdt å merke seg at vi skriver ut et enkeltelement inni an array som $array[1] og ikke som @array[1]! Grunnen til dette er at vi vanligvis ønsker å få tak i en enkel verdi, og ikke en array! Om du absolutt må ha en array som resultat, kan du gjøre som så:

my @nok_en_array = @array[0,2];
print "@nok_en_array" # Vi får "a c"
my @enda_en_array = @array[0..2];
print "@enda_en_array" # Skriver ut "a foo c"

Utover dette finnes det en del funksjoner som behandler "arrays", og er par av disse er:

push(@array, "verdi2");
my $var1 = pop(@array);
unshift(@array, "verdi2");
my $var2 = shift(@array);

Et eksempel uten videre dyp forklaring...

my @array = ("a", "b", "c");
my $last = pop(@array);
push(@array, "e");
my $first = shift(@array);
unshift(@array, "d");
print @array; # Skriver ut "dbe"
 
foreach my $verdier (@array) {
        print "$verdier\n";
}



Del 8: Hashes (assosiative arrays?)

En assosiativ array, eller "hash", består av tilordnede nøkkel- og verdi-par hvor nøkkelen peker til en verdi:

my %hash = (key1 => 1,
            key2 => "tekst",
	    key3 => 3);
print $hash{key1};     # Skriver "1"

Merk at vi også her bruker '$' for å trekke ut en skalar verdi. For å gå igjennom alle verdiene i en hash så kan man bruke følgende prosedyre:

foreach my $key (keys %hash) {
        print "$key => $hash{$key}\n";
}

Denne vil gå igjennom alle nøklene og verdiene for deretter å skrive dem ut. Nøkkelfunksjonen her er keys (pun intended), som returnerer alle nøklene i %hash (tilsvarende finnes det en en values funksjon). Man kan selvfølgelig utvide dette for å gjøre mer komplekse ting med hashen f.eks:

my %count_hash;
foreach my $key (sort keys %hash) {
        # teller antall forekomster av bestemte verdier i en hash.
        ++$count_hash{$hash{$key}};
}



Del 9: Subrutiner i perl

Bruk my();! Dette er spesielt viktig i funksjoner så variablene er unike for den navnerom de blir deklarert i. Godt tips som du allerede bør vite: Bruk aldri globale variabler! De skaper bare problemer.

print pluss(7, 77);  # Skriver ut "84"

sub pluss {
        my( $argument1, $argument2 ) = @_; # Argumenter ligger i @_
        return $argument1 + $argument2;
}

print pluss(4, 19);  # Skriver ut "23"

Det er også likegyldig hvor i programemt vi definerer funksjonen - det er ditt ansvar å skrive ryddig kode!



Del 10: Hvordan kjøre et perl-script

Følgende punkter må være i orden for at programmet skal kunne kjøre:

  • Maskinen du logger inn på på ha perl installert! Dette kan du finne ut med which perl.
  • Pass på at du har en ny versjon av perl. Dette kan du finne ut med perl -v. Versjon 5.004_04 eller nyere er bra, 5.003 eller nyere går an, og om du har noe eldre en det, oppgrader. Om du skulle være uheldig nok til å ha en versjon 4 perl, så bli kvitt den med en gang! Perl 4 er død, og vil aldri mere bli oppdatert!
  • Lag en fil og plasser i toppen av fila. Første bokstav i fila skal være "#"! Strengt tatt er ikke -wT, use strict; og use diagnostics; nødvendig, men de er fryktelig nyttige å ha der mens du lager programmet - når du er ferdig kan du ta dem vekk igjen.


#!/usr/bin/perl -wT

use strict;
use diagnostics;

  • Pass på å erstatte /usr/bin/perl med resultatet til which perl om det er ulikt, ellers vil du ikke få kjørt programmet.
  • Det er viktig at fila er eksekverbar! Dette fikser du med chmod(1) kommandoen. man chmod hjelper deg der om du ikke er kjent med kommandoen.



Del 11: Spesielle variabler

De "spesielle variablene." De er ofte sett på som det "mest ekle" ved perl, og det er de som tillater å skrive et program virkelig raskt - på godt og ondt. Du slipper ikke unna dem! En spesiell detalj ved disse, er at du ikke kan bruke my(); for å gjøre dem til lokale variabler. Som oftest gjør ikke det noe, men om du f.eks. ønsker å endre an spesialvariabel inni en subrutine, uten at "orginalen" blir ødelagt, bruk local();. Merk at det finnes mange fler, men det er som oftest disse du vil møte først.

@ARGV

...Er et predefinert array som inneholder kommandolinjeargumentene. Om du kjører perl-scriptet test.pl med parameter "start" vil argumentet bli plassert $ARGV[0] (og tilsvarede $ARGV[1] for det andre argumenter, osv.)

@_

@_ er et predefinert array som brukes til å gjøre argumentene til en funksjon tilgjengelig inni funksjonen. Dette vil si at alle subrutiner mottar argumentene sine i et array - noe som tillater en vilkårlig mengde parametere!

sub foo { print $_[1] }   # Skriver ut andre argument i @_

foo("A", "B", "C");       # "B" blir skrevet ut!

$_

$_ er meget spesiell. Dette er "default-variabelen"! Svært mange funksjoner i perl godtar å bli kjørt uten parametere i det hele tatt, eller uten å vise til hvilke variabler som blir behandlet. Når dette er tilfelle, vil alltid innholdet i $_ bli brukt!

$_ = "En test\n";
print;      # Skriver ut "En test\n"

my @array = ( 1, 1, 2, 3, 5, 8, 13, 21 );
foreach ( @array ) { # Et og et element i @array blir plassert i $_
        print;       # ... og skrevet ut!
}

$!

Denne variabelen inneholder feilmeldingsteksten til den siste feilen som oppstod. For eksempel om du prøver å åpne en fil for lesing, og feiler, vil årsaken til feilen stå der.

my $filnavn = "/etc/passswd";
open( FIL, "<$filnavn" ) || die "Kan ikke lese fra '$filnavn': $!\n";

$0

$0 inneholder programnavnet.

%ENV

Inneholder miljøvariablene! Vil du vite hva $USER er satt til? print $ENV{USER};!

$@

Inneholder en eventuell feilmelding fra filsystemet om et system-kall har feilet. (Mere om system() kommer senere!)

system("ls -l", "/dev/nosuchthing", "/etc", "/usr");
die "Error: $@\n" if defined $@;



Del 12: Exterior: Dagobah -- day

With Yoda strapped to his back, Luke climbs up one of the many thick vines that grow in the swamp until he reaches the Dagobah statistics lab. Panting heavily, he continues his exercises -- grepping, installing new packages, logging in as root, and writing replacements for two-year-old shell scripts in Python.

Yoda:

Code! Yes. A programmer's strength flows from code maintainability. But beware of Perl. Terse syntax... more than one way to do it... default variables. The dark side of code maintainability are they. Easily they flow, quick to join you when code you write. If once you start down the dark path, forever will it dominate your destiny, consume you it will.

Luke:

Is Perl better than Python?

Yoda:

No... no... no. Quicker, easier, more seductive.

Luke:

But how will I know why Python is better than Perl?

Yoda:

You will know. When your code you try to read six months from now.

(Funnet på rec.humor.funny, og gjengitt uten tillatelse siden forfatter er ukjent. ;^)



Del 13: Enda flere spesielle variabler

Åh nei...

Joda. Det finnes mange, og vi tar et par til som er kjekke å vite om.

$/

$/ kalles "the input record seperator", og bestemmer hva som skal være skilletegnet mellom hvert element su leser fra en filehandle. Standardverdi er "\n", som medfører at man leser kun en linje om gangen.

open( PASSWD, "/etc/passwd" ) || die "Kan ikke lese: $!";

my $line = <PASSWD>;
print $line;
# "navnsen:sEp4D.g/dg:6000:133:Etter Navnsen:/home/navnsen:/bin/false\n"

local( $/ ) = ":";

my $field = <PASSWD>;
close( PASSWD );

print $field;
# "navnsdottir:"

$\

"Output record seperator" er skilletegnet mellom hvert element i et array! Standardverdi er "", så det dukker ikke opp noe mellom hvert element.

my @stuff = qw( datamaskin sykkel dress bestikk );

print @stuff;
# "datamaskinsykkeldressbestikk"

local( $\ ) = " OG ";

print @stuff;
# "datamaskin OG sykkel OG dress OG bestikk"



Del 14: Filbehandling

Hvordan leser man fra en fil?

Pass på å sjekke returverdier, hva en du åpner!

open( FILE, "<fil.txt" )
    || die "Kan'ke lese fila 'fil.txt': $!\n";
while ( <FILE> )  {
        # Gjør noe nyttig her.
	print;
}
close FILE || die "Kainn ittj' lukk fil.txt? $!\n";

Hvordan skriver man til en fil?

Sjekk av returverdi! Sjekk returverdi!

open( FILE, ">fil.txt" )
    || die "Kan'ke skriv te 'fil.txt': $!\n";
print FILE "første linje\n";
close FILE || die "Kainn ittj' lukk fil.txt? $!\n";



Del 15: open()

En liten oversikt over hva open() kan gjøre

Man bruker open til å kjøre klar filen for skriving eller lesing, og oppretter i prosessen en "filehandle" som man bruker under resten av behandlingen. I vårt tilfelle heter den FILE. Her kommer en liten oversikt over de forskjellige måtene å bruke open på:

open(F, "fil");         # lesing
open(F, "<fil");        # lesing -eksplisitt
open(F, ">fil");        # skriving
open(F, "+>fil");       # les og skriv
open(F, ">>fil");       # tillegg (legge til på slutten)
open(F, "|cmd");        # skriv til kommando
open(F, "cmd|");        # les fra kommando
open(F, "|-");          # skriv til fork
open(F, "-|");          # les fra fork

The diamond operator

En spesiell filehandle en "the diamond operator" <>. Om programmet ditt ikke tr noen argumenter vil <> lese fra STDIN. I motsatt tilfelle vil alle argumentene kunne tolkes som filnavn og automatisk leses inn. Det går ikke an å skrive til "the diamond operator".

#!/usr/bin/perl -w
# Et lite program som kommenterer ut hele filer.
#
use strict;

while ( <> ) {
        print "# $_";
}



Del 16: Betingelser og Kontrollstrukturer

Perl inneholder selvfølgelig de fleste kontrollstrukturene man har bruk for...

#!/usr/bin/perl -w

print "Skriv et tall: ";
$tall = <STDIN>;
chomp($tall);          # fjerne linjeskift
if ( $tall > 0 ) {
        print "positivt tall\n";
elsif ($tall < 0) {
        print "negativt tall\n";
} else {
        print "tallet er 0\n";
}

Forøvrig finnes det et par andre varianter av if-testen:

unless ($tall == 2) {
        print "tallet er ikke 2";
}

# Prøv "baklengs" notasjon! :
print "God dag!\n" $natt && $a_menneske;

# Eller hva med "kort-notasjonen"?
my $tall = 0;
print $tall ? "sann\n" : "usann\n";



Del 17: Logiske operatorer

Perl har to logiske operatorer, && (AND) og || (OR). Begge er kortsluttende! dette betyr at "andre halvdel" ikke blir sjekket med mindre første halvdel er henholdscis sann (for AND) eller usann (for OR). I tillegg finnes or og and, men ikke bruk de, for de fungerer på en litt annerledes måte.

$tall == 2 && print "tallet er 2\n";
$tall == 2 || print "tallet er ikke 2\n";



Del 18: Sannhetsverdier

Hva er sant?

Vi snur heller på spørsmålet - Hva er usant?

print "Dette skal ikke skrives ut\n"
    if ("" || "0" || 0 || undef);

Alt annet tar perl som god fisk - om du fikk 1, "ingen" eller "-99" fisk vil perl være enig med deg. En liten detalj: perl tror deg dersom du sier du fikk "0.0" fisk! Dvs. "0.0" er sann... :)



Del 19: Løkker

While og Until-løkker

while-løkker blir kjørt såfremt startbetingelsen er møtt. I det startbetingelsen ikke lenger er sann vil programmet gå ut av løkken.

my $tall = 0;
while ($tall < 10) {
        $tall++;
        print "nå er løkken kjørt $tall ganger\n";
}

Løkker kan også skrives som ...

until( $event ) {
        # gjør noe her.
}

... som tilsvarer while( ! $event )

For-løkker

Denne bør være kjent.

for( $i = 0; $i <= 10; $i++ ) {
        print $i;
}



Del 20: Kall på eksterne programmer

`backticks` og system()

Ikke sjeldent vil det være interessant å starte opp eksterne programmer i et perl-script. For eksempel ønsker man å sende et e-brev, og starter sendmail for å gjøre dette, eller man vil ha en oversikt over hvilke prosesser som er i gang på maskinen. Det finne flere måter å gjøre dette på, og 'backticks' er en:

my $tidspunkt = `date`;   # Henter klokkeslett og dato fra date(1)
			  # og legger den i $tidspunkt

Det er bakoverfnuttene (``) som forteller perl at den skal kjøre en system-kommando. Bakoverfnuttene fungerer også som vanlige fnutter, ved at variabler blir interpolert.

my $foo = system("date"); # Skriver ut (!) dato, og setter $foo
                          # til enten 0 eller et tall med statuskoder

Ta en titt på perldoc -tf system om du trenger å forstå returverdiene.

exec() - og open() igjen

exec() gjør det samme som system() men den eneste forskjellen at perl-prosessen din ditt blir erstattet av programmet du exec()'er.

exec( $program, "arg1", "arg2" );

I tillegg kan man bruke open() som vi husker fra filbehandlingsdelen:

open( LS, "ls|" ) || die "Kunne ikke åpne ls: $!\n";



Del 21: Faremomenter ved kall på eksterne programmer!

Hva kan gå galt?

Gitt følgende uheldige scenario:

my $argument = '/etc; rm -rf / >&- 2>&- &';
        # Dette kan komme fra en ekstern kilde! kanskje du
	# leste inn parametere fra <STDIN>?

system("ls -l $argument");  # Oops!

Ingen ønsker å oppleve dette - derfor er det viktig å passe på å lage perl-programmene sine skikkelige - SPESIELT HVIS ANDRE SKAL BRUKE DEM!!

Hvordan unngå å drite seg ut?

Et par gode triks er:
  • Unngå å bruke ``, system(), exec(), open(), opendir(), syscall(), glob() og andre funksjoner som aksesserer systemet.
  • Bruk -T (taint-checking) som parameter til perl!
  • Om du må bruke system() og tilsvarende funksjoner, pass på å bruke lister som argumenter i stedet for tekststrenger. Om du bruker en tekststreng for to eller flere ord sammen, så vil perl starte et eget skall for å "gjøre fornuft" av parameterene. system("ls -l $dir1 $dir2") er farlig, men system("ls", "-l", $dir1, $dir2) er OK.



Del 22: Regulære uttrykk (regular expressions)

Perl sitt mest berømte våpen

Regexp er perl sin største og mest populære styrke. Man kan med svært enkle konstuksjoner representere komplekse data. Regulære uttrykk blir oftest brukt til analyse, eller uthenting og endring av data i store mengde tekst.

#!/usr/bin/perl -w
#
use strict;

while( my $logglinje = <> ) {
        print ++$teller,": $logglinje" if m/^mandag/;
}

Her er kortversjonen av hva som er mulig med regulære uttrykk:

bokstav         matcher seg selv
tall            matcher seg selv
\.              matcher et punktum
\tegn           matcher et tegn
\\              matcher \
.               matcher alle tegn unntatt "\n"
[abc]           matcher a, b eller c
[a-z]           matcher a, b,... y, z
[^abc]          matcher alt unntatt a,b eller c
\w              [a-zA-Z0-9_]
\W              [^\w]
\s              matcher "whitespace"
\S              alt annet en whitespace
\d              [0-9]
\D              [^0-9]
\n              matcher newline
\t              matcher tab
\b		matcher begynnelsen eller slutten av et ord
\032            matcher tegn tilsvarende oktalverdien gitt

For å enkelt lage kraftigere uttrykk trenger vi flere elementer ("Kvantifiserere"?).

*               matcher 0 eller flere ganger
+               matcher 1 eller flere ganger
?               matcher 1 eller 0 ganger
{n}             matcher nøyaktig n ganger
{n,}            matcher minst n ganger
{n,m}           matcher minst n og maks m ganger

Grådige uttrykk

I utgangspunktet er alle "Kvantifiserere" grådige - de ønsker å matche mest mulig, og om de få sjansen gjør de det. Det er ikke alltid vi er interessert i dette - noen ganger er vi f.eks. interessert i å få teksten mellom to apostrofer, men det er flere apostrofer på samme linje! For å unngå dette, kan vi legge til et spørsmålstegn etter "kvantifikatoren", så vil den prøve å matche minst mulig.

my $test = '"hah" sa han. Hunden ytret et "voff!"';
$test =~ m/"(.*)"/;
print $1;    # Skriver ut 'hah!" sa han. Hunden ytret et "voff!'
$test =~ m/"(.*?)"/;
print $1;    # Skriver ut '"hah!"'

Det går også an å legge til "ankre"! Hvis vi vet at en bestemt tekst er ved begynnelsen eller ved slutten av en linje, går det an å bruke. I tillegg bør du vite at man kan

^               matcher begynnelsen av linjen
$               matcher slutten av linjen

Og for å utføre en operasjon med regulære uttrykk, kan du gjøre.

#!/usr/bin/perl -w

my @rest;
while( <> ) {
        s/\r//g;         # Fjerner ^M fra slutten av linja
	s/[æøåÆØÅ]/./g;  # Erstatter alle æ, ø og å med punktum
	tr/a-z/A-Z/;	 # Gjør om små bokstaver til store
	m/(ab){2,4}/i;	 # matcher på abab (el.l.) uten å bry
			 # seg om store eller små bokstaver
	push(@rest, $_);
}

my @foos = grep( ! /foo/i, @rest ); # vi trenger linjene uten "foo"

Regexp-operatorer

Du kan forandre hvordan et regulært uttrykk fungerer ved å legge til en eller flere operatorer etter uttrykket. De vanligste er m//gimsx; - det finnes fler, men de bryr vi oss ikke om nå.

g               Matcher uttrykket globalt
i		Ignorere store/små bokstaver
m		Behandle dataene over flere linjer
s		Behandle dataene som om de var på en linje
x		Ignorere "unescaped whitespace" - Gjør 



Del 23: Nøstede datastrukturer

Referansemagi

Nøstede datastrukturer, det vil si datastrukturer inni datastrukturer lager vi med hjelp av referanser. Eksempler kan være flerdimensjonelle arrays eller hasher av hasher.

my @array = ('foo',
             'bar',
	     ['a', 2, 'c'],
	     {key1 => "verdi1",
	      key2 => "verdi2"}
	    );

Her har vi en array som inneholder fire elementer. De to første er skalare, det tredje er en anonym array, og det fjerde en anonym hash. Gitt koden ovenfor, er følgende utsagn sanne:

$array[0} == 'foo';
$array[2]->[1] == 2;
$array[3]->{key2} == 'verdi2';

Vi kan lage en referanse til @array, og om vi gjør det vil ting bli mer interessante. Gitt første uttrykk her, så er resten sanne:

my $aref = \@array;
$aref->[1] == 'bar';
$aref->[3]->{key1} == 'verdi1';

Alt dette kan kombineres hvordan du vil - bar husk å holde orden!



Del 24: Moduler og 'package'

En enkel modul

Prøv! Dette er ikke komplisert.

# Vi starter med å lage en fil som vi kaller Hello.pm

package Hello;   # Samme navn som fila, men uten ".pm"

# Om fila het Hello/World.pm, må vi kalle pakken Hello::World

sub person {
        my $navn = shift;
	my $kjonn = shift;

	die "person() skal ha 2 argumenter" unless @_ == 0;
	die "$navn sitt kjønn må være 'M' eller 'K'! (ikke '$kjonn')\n"
	    unless $kjonn =~ m/^[MK]$/;

	return [ $navn, $kjonn, 0 ]; # siste tallet er en "hilse-teller"
}

sub hils {
        my $person = shift;  # vi venter oss returverdien fra person()
	my $hva = $person->[1] eq "M" ? "han" : "hun";
	my $hvem = $person->[0];

	$person->[2]++;  # hvor mange ganger $person er blitt hilst på?

	print "Nei, se! Der har vi jo $hva $hvem!\n";
}

sub vis_ant_hilsninger {
        my $person = shift;
	my $hvem = $person->[0];
	my $ant = $person->[2];
        print "Vi har hilset på $hvem $antall gang",
	    ( $ant-1 ? "" : "er" ), "\n";
}

1;   # det er viktig at alle moduler returnerer en sann verdi!



# Ny fil! Dette er "hils.pl"

package main;   # Hoved-navnerommet heter "main", og trengs ikke å
		# annonseres på denne måten, egentlig. Med mindre vi
		# har flere "package" i samme fil...

my $friend1 = Hello::person("Gorm", "M");

my @other_friends = (
        Hello::person("Natalija", "M"),
	Hello::person("Remi", "M") );

# Og så hilser vi po et par venner :)

Hello::hils( $friend );
Hello::hils( $other_friends[0] );
Hello::hils( $other_friends[1] );
Hello::hils( $friend );
Hello::hils( $other_friends[0] );
Hello::hils( $friend );
Hello::vis_ant_hilsninger( $friend );
Hello::vis_ant_hilsninger( $other_friends[0] );
Hello::vis_ant_hilsninger( $other_friends[1] );

__END__

Ferdig! Vi kunne ha fått penere kode ved å importere symbolene fra
Hello.pm, men da mister vi fordelen av å vite nøyaktig hvilken pakke
funksjonene hører til i.



Del 25: OO-programmering

Litt om hva som finnes

Objekt-orientert programmering i perl er en litt pussig opplevelse. En del av OO-teorien er blitt droppet! - det finnes f.eks. ikke egne "public" eller "private" -egenskaper. Enkapsulasjon må programmereren selv sørge for å ikke bryte. Perl-objekter har likevel de viktigste elementene som modularitet, arv (også multippel arv, om vi trenger det), polymorfisme og annet. En klasse i perl lages ved å opprette en fil med en egen pakke i seg, og passe på at det finnes en konstruktor. Funkjonene i pakken er da metodene i klassen, globale variabler i pakkens navneromm bli klasse-variabler, og variablen konstrukoren returnerer blir objekt-variabler. Verre er det ikke!

# Vi vi prøver oss på nytt, og lager en fil som vi kaller Hello.pm

package Hello;   # Som før...

sub new {   # Vanlig navn på en konstruktor er "new" - ingen tvang!
        my $package = shift;  # første argument er alltid en referanse
			      # til 
        my $navn = shift;
	my $kjonn = shift;

	die "new() skal ha 2 argumenter" if @_;
	die "$navn sitt kjønn må være 'M' eller 'K'! (ikke '$kjonn')\n"
	    unless $kjonn =~ m/^[MK]$/;

	my $person = {
	        navn      => $navn,
		kjonn     => $kjonn,
		_ant_hils => 0  };

	bless( $person, $package ); # Her knytter vi $person til
				    # $package, slik at vi får
				    # effekten av en klasse.
	return $person;
}

sub hils {
        my $self = shift;
	my $hva = $self->{kjonn} eq "M" ? "han" : "hun";
	my $hvem = $self->{navn};

	$self->{_ant_hils}++;  # hvor mange ganger "jeg" er blitt hilst på?

	return "Nei, se! Der har vi jo $hva $hvem!\n";
}

sub vis_ant_hilsninger {
        my $self = shift;
	return $self->{_ant_hils};
}

sub vis_navn {
        my $self = shift;
	return $self->{navn};
}

sub vis_kjonn {
        my $self = shift;
	return $self->{kjonn};
}

1;   # Så avslutter vi med en sann verdi.



#!/usr/bin/perl -w
# Ny fil! Dette er "hils.pl"

use strict;
use diagnostics;

use Hello; # Hello.pm må være tilgjegelig i @INC for at dette skal
           # fungere smertefritt.

my $friend = Hello->new("Gorm", "M");

my @other_friends = (
        Hello->new("Natalija", "M"),
	Hello->new("Remi", "M") );

# Og så hilser vi po et par venner :)

print $friend->hils();
print $other_friends[0]->hils();
print $other_friends[1]->hils();
print $other_friends[1]->hils();
print $friend->hils();
print $other_friends[1]->hils();
print $other_friends[0]->hils();

print $friend->vis_ant_hilsninger();
print $other_friends[0]->vis_ant_hilsninger();
print $other_friends[1]->vis_ant_hilsninger();

__END__



Del 26: En CGI-modul

CGI_Lite

En lett-versjon av den (mye) kraftigere CGI-modulen i perl - denne passer bedre til enkle oppgaver, og er ikke så tung for systemet.

#!/usr/bin/perl -wT
#
# Et enkelt CGI-script som returnerer data du har gitt.

use strict;
use diagnostics;
use CGI_Lite;

my $cgi = new CGI_Lite; # En annen måte å lage et objekt på

$cgi->set_platform("Unix");
$cgi->add_mime_type("text/html");

my %form = $cgi->parse_form_data; # Hente inn data fra browser

$/ = undef; # Vi vil ha alt på en gang når vi leser fra en filehandle

my $page = <DATA> # Leser fra __DATA__ nedenfor

foreach my $data_key (keys %form) {
        # Bytte ut f.eks. $NAVN med $form{NAVN}
        $page =~ s/\$$data_key/$form{$data_key}/gm;
}

$page =~ s/\$\w+/foo/gm;

print "Content-type: text/html\r\n\r\n";
print $page;

__DATA__
<HTML>
<HEAD><TITLE>CGI_Lite-test</TITLE></HEAD>
<BODY>

<H1>Hei, $NAVN</H1>

Du er $ALDER gammel, har jeg hørt!

<HR>

<FORM METHOD="POST" ACTION="$SCRIPT_URL">
Navn: <INPUT TYPE="text" NAME="NAVN" VALUE="$NAVN"><BR>
Alder: <INPUT TYPE="text" NAME="ALDER" VALUE="$ALDER"><BR>
<INPUT TYPE="submit">
</FORM>

</BODY>
</HTML>



Del 27: Referanser

Nyttige linker

Online dokumentasjon

  • perldoc er perl sitt plattformuavhengige dokumentasjons-system - prøv å kjøre perldoc perl!
  • perldoc
    • perl - For å få oversikten.
    • perlstyle - Hvordanskrive ryddig kode.
    • perlfaq - Mange spørsmål med gode svar!
    • perldata - Datastrukturer
    • perlre - Regulære uttrykk
    • perlfunc - Funksjoner Extravaganza!
    • perlrun - Hva kan du skrive på kommandolinja?
    • perltoot - Et mini-kurs i Objekt-orientert perl!
    • perlsec - Om sikkerhet, og hva du bør tenke på.
    • perlpos - Hvordan lage enkel, profesjonell inline dokumentasjon.
    • perlvar - Spesialvariabler...
    • perltrap - Programmeringsfeller man kan gå i.
    • perlmod - Hvordan man bruker moduler i perl.
  • Prøv også perldoc -tf funksjonsnavn!

Bøker

  • "Learning Perl" - O'Reilly & Associates - ISBN: 1-56592-284-0
  • "Programming Perl" - O'Reilly & Associates - ISBN: 1-56592-149-6
  • "Perl Cookbook" - O'Reilly & Associates - ISBN: 1-56592-243-3
  • "Mastering Regular Expressions" - O'Reilly & Associates - ISBN: 1-56592-257-3
  • "Object Oriented Perl" - Manning Publications - ISBN: 1-88477-779-1



Del 28: XML::Parser og XML::RSS

'Mine overskrifter'

Scriptet, beskrivelse av det og eksempler på bruk kan du finne på http://www.webreference.com/perl/tutorial/8/ .

#!/usr/bin/perl -w
# rss2html - converts an RSS file to HTML
# It take one argument, either a file on the local system,
# or an HTTP URL like http://slashdot.org/slashdot.rdf
# by Jonathan Eisenzopf. v1.0 19990901
# Copyright (c) 1999 internet.com Corp. All Rights Reserved.
# See http://www.webreference.com/perl for more information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# INCLUDES
use strict;
use XML::RSS;
use LWP::Simple;

# Declare variables
my $content;
my $file;

# MAIN
# check for command-line argument
die "Usage: rss2html.pl (<RSS file> | <URL>)\n" unless @ARGV == 1;

# get the command-line argument
my $arg = shift;

# create new instance of XML::RSS
my $rss = new XML::RSS;

# argument is a URL
if ($arg=~ /http:/i) {
    $content = get($arg);
    die "Could not retrieve $arg" unless $content;
    # parse the RSS content
    $rss->parse($content);

# argument is a file
} else {
    $file = $arg;
    die "File \"$file\" does't exist.\n" unless -e $file;
    # parse the RSS file
    $rss->parsefile($file);
}

# print the HTML channel
print_html($rss);

# SUBROUTINES
sub print_html {
    my $rss = shift;
    print <<HTML;
<TABLE BGCOLOR="#000000" BORDER="0" WIDTH="200"><TR><TD>
<TABLE CELLSPACING="1" CELLPADDING="4" BGCOLOR="#FFFFFF"
 BORDER=0 WIDTH="100%">
  <TR>
  <TD VALIGN="middle" ALIGN="center" BGCOLOR="#EEEEEE"><FONT
   COLOR="#000000" FACE="Arial,Helvetica"><B><A
   HREF="$rss->{'channel'}->{'link'}">$rss->{'channel'}->{'title'}</A>
</B></FONT></TD></TR>
<TR><TD>
HTML

    # print channel image
    if ($rss->{'image'}->{'link'}) {
	print <<HTML;
<CENTER>
<P><A HREF="$rss->{'image'}->{'link'}"><IMG
 SRC="$rss->{'image'}->{'url'}" ALT="$rss->{'image'}->{'title'}"
 BORDER="0"
HTML
        print " WIDTH=\"$rss->{'image'}->{'width'}\""
	    if $rss->{'image'}->{'width'};
	print " HEIGHT=\"$rss->{'image'}->{'height'}\""
	    if $rss->{'image'}->{'height'};
	print "></A></CENTER><P>\n";
    }

    # print the channel items
    foreach my $item (@{$rss->{'items'}}) {
	next unless defined($item->{'title'})
	            && defined($item->{'link'});

	print "<LI><A HREF=\"$item->{'link'}\">".
	    "$item->{'title'}</A><BR>\n";
    }

    # if there's a textinput element
    if ($rss->{'textinput'}->{'title'}) {
	print <<HTML;
<FORM METHOD="GET" ACTION="$rss->{'textinput'}->{'link'}">
$rss->{'textinput'}->{'description'}<BR> 
<INPUT TYPE="text" NAME="$rss->{'textinput'}->{'name'}"><BR>
<INPUT TYPE="submit" VALUE="$rss->{'textinput'}->{'title'}">
</FORM>
HTML
    }

    # if there's a copyright element
    if ($rss->{'channel'}->{'copyright'}) {
	print <<HTML;
<P><SUB>$rss->{'channel'}->{'copyright'}</SUB></P>
HTML
    }

    print <<HTML;
</TD>
</TR>
</TABLE>
</TD></TR></TABLE>
HTML
}



Del 29: Annet?

Så kan man spørre...

perl -e 'print "Hva nå?\n";'