Robert's Perl Tutorial

Download as pdf or txt
Download as pdf or txt
You are on page 1of 86

4/10/12 Robert's Perl Tutorial

1/86 www.sthomas.net/roberts-perl-tutorial.htm
GoogleSearch

Web www.sthomas.net
TableofContents
Home
Introduction
AShortIntroductionToPerl
Setup
TheTutorial:TheJourney
Begins
Comparisons
UserInput
Arrays
DeletingVariables
BasicRegularExpressions
SubsitutionandYetMore
RegexPower
SplitandJoin
Arecap,butwithsomenew
functions
Files
ReadingDirectories
AssociativeArrays
Sorting
GrepandMap
ExternalCommands
Oneliners
SubroutinesandParameters
Modules
BondageandDiscipline
Debugging
LogicalOperators
Lastwords
Thanksto...
Robert'sPerlTutorial
Version4.1.1THISDOCUMENTISCOPYRIGHTED.
Reproductioninwholeorpartisprohibited.Pleaseemailmeat(emailaddressisnolongervalid)ifyouwanttousethisinformationanywhere.
Thisdocumentismirroredfromhttp://www.netcat.co.uk/rob/perl/win32perltut.htmlwithpermission.
Robert'sPerlTutorial
Version4.1.1
Thelasthackwasmadeon:20thApril1999
soHenrycanconcentrate
Introduction
Thistutorialis...
AbasicPerlcourseprimarilyforuseonWin32platforms.Itassumesthatthereaderknowsnothingof
programmingwhatsoever,butneedsasolidgroundingforfurtherwork.Afteryoufinishthiscourseyou'llbe
readytospecialiseinCGI,sysadminorwhateveryouwanttodowithPerl.
Thistutorialisnot...
Areferencemanual.Youwon'tfindalltheregexstuffunderRegex.Ithinkit'smorefuntolearnthe
basicsthenaddlittleextrasalongtheway.Keepsyouawake,anditisagoodexcusefornotorganising
itbetter.
AFAQ.
Politicallycorrect.
Complete.Pleasedon'tfinishthecourseandassumeyouknowallthereistoknowaboutPerl.Thereis
certainlyenoughheretogetyoustarted,butconsiderthecontentsofthiscourseasthetipofthe
iceberg.Exceptmaybealittlewarmer.
Erorthree.
TheTableofContents
I'vehadafairamountofrequestsforaToC,sohereitis:
SolarModulesSupplierwww.chinasunergy.com
SpecializedinSolarModulesHigh
efficiency,Competitiveprice.
Carbonnanotubeswww.nanoamor.com
Single.double,mutilwalled.
Aligned/functionalized/graphitized
EasyWebFormBuilderwww.myContactForm.com
Useourwizardtobuildwebforms$39.99/year,no
ads,CAPTCHA!
Freedatasheetsearchwww.datasheets.com
185milliondatasheets,inventoryandpart
comparisons.Checkitout!
4/10/12 Robert's Perl Tutorial
2/86 www.sthomas.net/roberts-perl-tutorial.htm
Introduction
Thistutorialis...
Thistutorialisnot...
HowtoUse...
TheTableofContents
ConventionsusedinthisTutorial
Whatyouneedtoknow
Useofthisdocument
PersonalPrintouts
Intranetusage
Mirroring
Translations
AShortIntroductionToPerl
WhatisPerl?
WhatisActivePerl?AretheotherPerlsinactive?
CanIrunPerlonmycomputer?
WhatcanIdowithPerl?
TheInternet
SystemsAdministration
Whatcan'tIdowithPerl?
Support
Setup
1.GettingtheSoftware
2.Installation
3.TestingYourFirstPerlScript
TheTutorial:TheJourneyBegins
YourFirstTime
Whatifitdoesn't...?
Assumingit'snowallright...
Shebang
Variables
Scalars
$%@areGoodThings
Typing
VariableInterpolation
ChangingVariables
Auto(de|in)crements
Escaping
Context:AboutPerland@^$%&~`/?
StringsandIncrements
Print:AListOperator
SubroutinesAFirstLook
Comments
Comparisons
Aniffystart
TheTruthAccordingtoPerl
EqualityandPerl
AllEqualityisNotEqual:NumericversusString
AninterludeThePerlMotto
TheComparisonOperatorsListed
ReachMore
Readers
Letyourreaders
embedyourblog
postsandgrowyour
traffic
repost.us/
MachineWorks
Theleading
SoftwareToolkitFor
CNCSimulation
AndVerification
www.machineworks.com
Free
Programming
Courses
Getstartedin
minutes!Becomea
SoftwareEngineer
code.he.net
MIKE2.0
Methodology
Anopensource
methodologyfor
Enterprise
Information
Management
www.openmethodolo
4/10/12 Robert's Perl Tutorial
3/86 www.sthomas.net/roberts-perl-tutorial.htm
TheGoldenRuleofComparisons
MoreAboutIf:Multiples
elsif
UserInput
STDINandotherfilehandles
Chop
SafeChoppingwithChomp
Arrays
Lists,herdswhatarearrays?
BasicArrayWork
ElementsofArrays
Howtorefertoelementsofanarray
Morewaystoaccessarrays
ForLoops
AforLoopdemonstrated
Forloopswith..,therangeoperator
foreach
Theinfamous$_
APrematureEndtoyourloop
Alittlemorecontrolovertheprematureending:Labels
ChangingtheElementsofanArray
JiggerypokerywithArrays
Atableofarrayhackingfunctions
Splice
DeletingVariables
FalsevaluesversusExistence:Itis,therefore...
BasicRegularExpressions
Anintroduction
Senstivityregexesintouchwiththeirinnerchild
CharacterClasses
Matchingatspecificpoints
Negatingtheregex
ReturningtheMatch
*+regexesbecomelinenoise
TheDifferenceBetween+and*
Reusingthematch\1,$1...
HowtoAvoidMakingMountainswhileEscapingSpecialCharacters
SubsitutionandYetMoreRegexPower
Basicchanges
\w
Replacingwithwhatwasfound
x
MoreMatching
ParenthesesAgain:OR
4/10/12 Robert's Perl Tutorial
4/86 www.sthomas.net/roberts-perl-tutorial.htm
(?:OREfficiency)
Matchingspecificamountsof...
Pre,Post,andMatch
RHSExpressions
/e
/ee
AWorkedExample:DateChange
SplitandJoin
Splitting
AveryFAQ
WhatHumptyDumptyneeds:Join
Arecap,butwithsomenewfunctions
Randomness
Concatenation
Files
Opening
Anunforgivableerror
\\or/inpathnamesyourchoice
Readingafile
WritingtoaFile
Asimplewrite
Appending
@ARGV:CommandLineArguments
ModifyingaFilewith$^I
$/Changingwhatisreadinto$_
HEREDocs
ReadingDirectories
Globbing
readdir:Howtoreadfromdirectories
AssociativeArrays
TheBasics
AHashinAction
Whenyoushouldusehashes
HashHackingFunctions
MoreHashAccess:Iteration,keysandvalues
Sorting
ASimpleSort
NumericSortingHowSortReallyWorks
SortingMultipleLists
GrepandMap
Grep
Map
Writingyourowngrepandmapfunctions
ExternalCommands
Somewaysto...
4/10/12 Robert's Perl Tutorial
5/86 www.sthomas.net/roberts-perl-tutorial.htm
Exec
System
Backticks
Whentouseexternalcalls
OpeningaProcess
Quoteexecute
Oneliners
Ashortexample
Fileaccess
Modifyingfileswithaonelinerand$^I
SubroutinesandParameters
Parameters
Namespaces
VariableScope
myVariables
MultipleReturns
Local
Returningarrays
Modules
Anintroduction
File::Findusingamodule
ChangeNotify
YourVeryOwnModule
BondageandDiscipline
w
Shebang
usestrict
Debugging
LogicalOperators
or
Precedence:WhatcomesFirst
And
OtherLogicalOperators
Lastwords
Thanksto...
Whatyouneedtoknow
YouneedtobeabletodifferentiatebetweenaPCandatoaster.Noprogrammingexperienceisnecessary.Youdoneedto
understandthebasicsofPCoperation.Ifyoudon'tunderstandwhatdirectoriesandfilesarethenyou'llfindthisdifficult.Youmight
finditdifficultevenifyoudo:)
Youdoneedtoexercisethebraincells,andyouneedtime.
Whatyouneedtohave
APCwhichcanrunaWin32operatingsystem.That'sWindowsNT3.5,3.51,4.0orlater,orWindows95orWindows98.Not
4/10/12 Robert's Perl Tutorial
6/86 www.sthomas.net/roberts-perl-tutorial.htm
Windows3.1.Sorry.Now,youfinallyhaveareasontoupgrade.
YouneedtogetholdofacopyofPerl,soforthatyoumightneedanInternetconnection.Butifyoucangetitsomeother
way,youdon't.
Noe:Youdon'tevenneedaWin32PCifyouarecomfortableinstallingPerlunderotheroperatingsystemslikeLinux,butnotall
theinformationherewillberelevant.
Youdon'tneedacomplier.Perlisaninterpretedlanguage,whichmeansyouruncodedirectly,notcompileitthenrunit.
Howtousethistutorial...
Justworkthroughfromstarttofinish.
Generally,theexplanationfollowsthecodesample.Beforeyoureadtheexplanation,tryandworkoutwhatthecodedoes.Then
checkifyou'reright.Inthisway,you'llderivemaximumvaluefromthetutorialandexercisetheoldgreycellsalittle.
Whenyoufinish,pleasesendmeacritique.Infact,sendoneevenifyoudon'tfinish.Iappreciateallfeedback!PleasenoteIam
notasourceoffreetechnicalsupport.DonotemailmeyourgeneralPerlproblems.Ifyouwantsupport,askonUsenetorthe
ActiveStatemailinglists.Thatsaid,Iwelcomeproblemselaedoheoialielf.
ConventionsusedinthisTutorial
Thehumourisnonconventional.Ithink.Ofmoreimportance,thetextiscolouredstrangelyinplaces.Myintentionistoaidyour
comprehension,notattemptbeautification.Themeaningofthecolours:
Sometimesyou'llneedtotypesomethinginonthecommandline.Thesecommandswillbeingreen,forexample:
perl changeworld.pl parm1 datafile.txt
Codethatyoushouldloadintoyoureditorandrunisinblue(don'trunthisnow,it'sjustanexample):
while (<DATFILE>) {
printf "%2s : $_",$.
}

whenfunctionsarereferredtointhetext,theirnamesarehighlightedinred.Forexample,laterwediscoveraninteresting
functioncalledsplit.
Allthecodeexampleshavebeentested,andyoucanjustcut'n'paste(bravestatement).Ihaven'tlistedtheoutputofeach
example.Youneedtorunitandseeforyourself.Considerthiscourseinteractive.Consideritanywhichwayyoulike.
Useofthisdocument
PersonalPrintouts
Finebyme,feelfreeprinttoacopyforyourownuse.
Intranetusage
Justemailmeandletmeknow.
Mirroring
Again,allIaskisanemail.
Translations
Everysooftensomeoneofferstotranslatethetutorial.Nobodyhasactuallydoneso.Ifyouwantto,theconditionsare:
Youdon'tchangethetextotherthanwhatcanbereasonablyexpectedduringatranslation
Thecontent,formatandnoticesauthorshipremainsthesame
4/10/12 Robert's Perl Tutorial
7/86 www.sthomas.net/roberts-perl-tutorial.htm
Youcanadda'translatedby'noticeintheintroandattheend,plusyourownmessage
VersionnumbersarerespectedbuttheISOcodeforyourcountryisadded,eg3.3.2.ES
andyouneedtoemailmetodiscuss.
Rememberthisdocumentiscopyrightedandallassociatedrightsarestrictlyreserved.

RobertPepper
mailto:[email protected]
AShortIntroductionToPerl
IfoaleadndeandhaPelideignedodo,knoifeaeandlimiaionhenocankiphiemallbhighl
infomaieecion,oehichIlaboedlongandhadfohoehadidn'kno.Ifoaeealle,jmpoheSepSecion.
WhatisPerl?
Perlisaprogramminglanguage.PerlstandsforPracticalReportandExtractionLanguage.You'llnoticepeoplereferto'perl'and
"Perl"."Perl"istheprogramminglanguageasawholewhereas'perl'isthenameofthecoreexecutable.Thereisnolanguage
called"Perl5"thatjustmeans"Perlversion5".VersionsofPerlpriorto5areveryoldandveryunsupported.
SomeofPerl'smanystrengthsare:
Speedofdevelopment.Youeditatextfile,andjustrunit.Youcandevelopprogramsveryquicklylikethis.Noseparate
compilerneeded.IfindPerlrunsaprogramquickerthanJava,letalonecomparethecompletemodifycompilerunohno
forgotthatsemicolonsequence.
Power.Perl'sregularexpressionsaresomeofthebestavailable.Youcanworkwithobjects,sockets...everythingasystems
administratorcouldwant.Andthat'sjustthestandarddistribution.AddthewealthofmodulesavailableonCPANandyou
haveitall.Don'tequatescriptinglanguageswithtoylanguages.
Usuability.Allthatpowerandcapabilitycanbelearntineasystages.IfyoucanwriteabatchfileyoucanprogramPerl.You
don'thavetolearnobjectorientedprogramming,butyoucanwriteOOprogramsinPerl.Ifautoincrementingnonexistent
variablesscaresyou,makeperlrefusetoletyou.ThereisalwaysmorethanonewaytodoitinPerl.Youdecideyourstyleof
programming,andPerlwillaccommodateyou.
Portability.OntheSuperhighwaytothePortabilityPanacea,Perl'sPorschepowerspastJava'sjadedjalopy.Manypeople
developPerlscriptsonNT,orWin95,thenjustFTPthemtoaUnixserverwheretheyrun.Nomodificationnecessary.
EditingtoolsYoudon'tneedthelatestIntegratedDevelopmentEnvironmentforPerl.YoucandevelopPerlscriptswithany
texteditor.Notepad,vi,MSWord97,orevendirectofftheconsole.Ofcourse,youcanmakethingseasyanduseoneofthe
manyfreewareorsharewareprogrammer'sfileeditors.
Price.Yes,0guilders,pounds,dmarks,dollarsorwhatever.Andthepeertopeersupportisalsofree,andoftenfarbetter
thanyou'devergetbypayingsomecompanytoanswerthephoneandtellyoutodowhatyoujusttriedseveraltimes
already,thenlookupthesamereferencebooksyoualreadyown.
WhatisActivePerl?AretheotherPerlsinactive?
AcompanynamedActiveStateexiststoprovidePerltoolsfortheWin32environment.ActiveStateusedtobeActiveWare,and
beforethatitwassortofapartofHipCommunications.Itnowappearstobehappywithitscurrentname,havingnotchangeditfor
overayear.Win32means,atthetimeofwriting,Windows95,Windows98andWindowsNT.ItdoesnomeanWindows3.11,even
withWin32sinstalled.
PriortoPerlversion5.005,therewasoneversionofPerlforWin32,andanotherforalltheothersystems.Theotherversionwas
knownasthe"nativeversion".
TheWin32versionwasdevelopedbyActiveState,called"PerlforWin32"andtypicallylaggedslightlybehindthenativeversion.As
ofthe5.005release,PerlforWin32andthenativeversionhavemergedthenativeversionnowsupportsWin32directlyand
doesn'tneedanytweakingbyActiveState.
ActiveStatehavedropped"PerlforWin32"andrenamedtheirdistribution,whichcomeswithanInstallShieldinstaller,"ActivePerl".
Incidentally,afewmonthsbefore5.005mergethenativePerlversionwaschangedsoitwouldrunonWin32directly.Thisversion
wasbestknownbythecreator'sname,"GurusamySarathy".However,therewerestillquiteafewdifferencesbetweenitandPerl
forWin32,somanypeopleranboth.Themergebroughtthebestofbothworldstogether.
4/10/12 Robert's Perl Tutorial
8/86 www.sthomas.net/roberts-perl-tutorial.htm
CanIrunPerlonmycomputer?
Probably.PerlrunsoneverythingfromAmigastoMacintoshestoUnixboxen.PerlalsorunsonMicrosoftoperatingsystems,
namelyWindows95,Windows98andWindowsNT3.51andlater.ThereareversionsofPerlthatrunonearlierversionsofthese
operatingsystemsbuttheyarenolongerdevelopedorsupported.Seehttp://www.perl.com/forfulldetails.
WhatcanIdowithPerl?
Justtwopopularexamples:
TheInternet
Gosurf.Noticehowmanywebsiteshavedynamicpageswith.plorsimilarasthefilenameextension?That'sPerl.Itisthemost
popularlanguageforCGIprogrammingformanyreasons,mostofwhicharementionedabove.Infact,thereareagreatmany
moredynamicpageswrittenwithperlthatmaynothavea.plextension.IfyoucodeinActiveServerPages,thenyoushouldtry
usingActiveState'sPerlScript.Quitefrankly,codinginPerlScriptratherthanVBScriptorJScriptislikedrivingacarasopposedto
ridingabicycle.PerlpowersagooddealoftheInternet.
SystemsAdministration
IfyouareaUnixsysadminyou'llknowaboutsed,awkandshellscripts.Perlcandoeverythingtheycandoandfarmorebesides.
Furthermore,Perldoesitmuchmoreefficientlyandportably.Don'ttakemywordforit,askaround.
IfyouareanNTsysadmin,chancesareyouaren'tusedtoprogramming.Inwhichcase,theadvantagesofPerlmaynotbeclear.
Doyouneedit?Isitworthit?
AfteryoureadthistutorialyouwillknowmorethanenoughtostartusingPerlproductively.Youreallyneedverylittleknowledgeto
savetime.Imaginedrivingacarforyears,thenrealisingithasfivegears,notfour.That'sthesortofimprovementlearningPerl
meanstoyourdailysysadminery.Whenyouareproficient,youfindthedifferencelikerealisingthesamecarhasareversegear
andyoudon'thavetopushitbackwards.Perlmeansyoucanbelazier.Lazysysadminsaregoodsysadmins,asIkeeptellingmy
boss.
AfewexamplesofhowIusePerltoeaseNTsysadminlife:
Useraccountcreation.Ifyouhaveatextfilewiththeuser'snamesinit,thatisallyouneed.Createusernames
automatically,generateauniquepasswordforeachoneandcreatetheaccount,pluscreateandsharethehomedirectory,
andsetthepermissions.
Eventlogmunging.NThasgreatEventLogging.NotsogreatEventReading.YoucanusePerltocreatereportsonthe
eventlogsfrommultipleNTservers.
Anythingelsethatyouwouldhaveusedabatchfilefor,orwishedthatyoucouldautomatesomehow.Nowyoucan.
Whatcan'tIdowithPerl?
Thequestionis,"whatshouldn'tIdowithPerl".Writeofficesuitesisoneanswer.Perl,likemostscriptinglanguages,isaglue
languagedesignedforshortandrelativelysimpletasks.Justdon'tequatethisphilosophywithalackofpoweror"serious"features.
Support
SeetheFAQsatwww.perl.com.OfcoursethereareUsenetgroups,butalsomanymailinglists.MicrosoftWindowsuserswillbe
interestedinthosehostedbyhttp://www.activestate.com/whichdiscussallthingsPerlandWindows.
Please,beforeyouaskanyquestion,anywhere:
1. Makesureyoureadthegroupcharter.Manypeopleputtimeandeffortintothecreationofthosecharterintheinterestsof
efficientdiscussion,sodon'tdegradethediscussionqualityandinsultusbyignoringtheguidelines.
2. ReadtheFAQsatleasttwice.TryandfindrelatedFAQs.Tryhard.Youwon'tbepopularifyoupostaquestionstarting"I've
lookedatalltheFAQs..."andthenasksomethingthatactuallyiintheFAQs.Orthemanualforthatmatter.Believeme,it
willbepatentlyobvioustoallonthelistifyouhaven'tdoneyourhomework.
4/10/12 Robert's Perl Tutorial
9/86 www.sthomas.net/roberts-perl-tutorial.htm
3. Carefullyphrasethequestionsandprovidesourcecodebecauseifyoudothat,youmaywellendupsolvingthe
problemyourselfbecauseyouhavethoughtitthroughalittlemore.
ThinktoyourselfhonestlyifIwasabusyPerlProfessional,wouldIwanttoanswermyownquestion?
DoesitclearlystatewhatIwantananswerto?Preferablyjustonequestionatatime.AmIbeingunreasonable,forexampleasking
forsomeonetocodeitforme?HaveIshownevidencethatIhavetriedtohelpmyself?HaveImadeanymistakesingrammar?Isit
polite?Isthereenoughinformationintherefortheanswertobegiven?
Whyshouldyoucare?Well,ifyouaskpoorlyformedquestionsorthosealreadyansweredintheFAQ...let'sjustsayyouwon'tget
theanswersyouwant.Ifyoucareaboutyouronlinereputationandwastingotherpeople'stimetwomorereasons.
Setup
Therearefourstages:
1. Getthesoftware.
2. Installit.
3. RunatestScript.
4. Celebrateortroubleshoot.
1.GettingtheSoftware
AnoldeionofPelfoWin32iincldedihheWindoNTReoceKi.Iiadloofdae.Folloheepbelooge
aneeeion.Haingaidha,ocancompleeheoialihheReoceKieionboholdpgadeaoona
ocan.
Gotohttp://www.activestate.com/andfollowthelinkstodownloadActivePerl.Itwillbeasinglefile,andthenamewillbesomething
likeapi508e.exe.TheistandsforIntel.IfyouhaveanAlpha,downloadapaXXXe.exe.Ifyou'renotsure,downloadtheIntel
version.
The508eistheversionnumber,soexpectthistochangequiterapidly.Thefilesizewillbejustover5Mb,soitwilltakeawhileto
downloadviamodem.IfyouknowhowtouseFTP,tryftp.activestate.com/activeperl/.
WhenyoufindActivePerl,savethefileintoanydirectoryyouplease.Iliketoorganisemydownloadsintoc:\downloadsbutthatis
justpersonalpreference.AslongasActivePerlendsuponyourharddisksomewhereitdoesn'tmatter.
2.Installation
Soyounowhaveapixxxx.exe.Ifyouforgetwhereyousavedit,don'tpanic,justrunWindowsExplorerandsearchforapi*e.exe
1. Doubleclicktheapixxxx.exe.You'llseethefantasticActivePerlgraphicandbeadvisedtocloseallopenapplications
beforeproceeding.Thelizardthingisagecko,whichadornsthefamousO'Reillybook"LearningPerlonWin32Systems".
Thistutorialisaimedatamorebasiclevelthanthatbook,intermsoftheauthor'sknowledge,intendedaudienceandquality
ofhumour.
2. Agreetothelicenseagreementorcanceltheinstall,stopthistutorialanddenyyourselfanyhopeofhackership.
3. Destinationdirectoryiswhateveryouwant.IusuallyinstallPerlinc:\progs\perlratherthanc:\program files\perl
becausemanyWin32programsdon'tproperlyhandlelongfilenames,letalonethosewithspacesin.Oryoucouldacceptthe
default.Yourchoice.
4. SelectComponents.Allyou'llneedforthistutorialis"PerlforWin32Core",butinstallingthe"OnlineHelpand
Documentation"and"ExampleFiles"ishighlyrecommended.IfyourunInternetInformationServer(IIS)3orlater,or
PersonalWebServer(PWS),theninstall"PerlforISAPI"and"PerlScript"too,althoughdon'ttryeitheroftheseuntilyouare
proficientwiththebasics.Thephraserunningbeforewalkingcomestomind.
5. SelectOptions.
"Associate'.pl'withPerl.exe".Ifyouselectthisoptionthenyoucanjusttypeinthenameofascriptatthecommand
line,ordoubleclickitandthescriptwillrun.Ifyoudon't,theninordertogetascripttoexecuteyou'llneedtotype:
perl myscript.pl
toexecutemyscript.pl.Personally,IpreferdoubleclickingtoallowmetoeditthefilesoIdonotselectthisoption.
Also,perlhasaplethoraofcommandlineargumentswhicharedifficulttopasstoascriptifyourunitbyassociation.
ForthepurposesofthistutorialI'massumingthatyouhaven'tassociated.plwithperl.
"AddthePerlbindirectorytoyourpath".Dothis,otherwiseyou'llhavetospecifythefullpathtoperl.exeeverytime
4/10/12 Robert's Perl Tutorial
10/86 www.sthomas.net/roberts-perl-tutorial.htm
youuseit.Notfun.
"StandardI/OredirectionforIIS".IfyourunIISorPWS,selectthis.ItisaGoodThing.Understanditlater.
6. IISOptionsIfyouuseIISorPWSyou'llhavethisscreenjustacceptbothoptions.
7. ProgramFolderwhateveryourpreferenceis.Thisisjustalinktothedocumentation,totheperl.exeitself.
8. Confirmationmakesurethatwhatisdisplayediswhatyouhaveselected...
9. Theinstallprogramwillnowcopyfiles.Attheenditwillrunafewperlscriptsitself,whichbrieflyappearasDOSboxes.Don't
worry,itisallquitenormal.
10. Releasenotes.Wellwortharead.
11. Reboot!Justsothepathstatementtakeseffect.Inanycase,itisalwaysgoodpracticetorebootafteranewinstall.
3.TestingYourFirstPerlScript
Soyouknowwhatthistutorialisdesignedtodo.YouknowwhatPerlisdesignedtodo,andyouhaveeveninstalledit.Itisnowtime
tostartthetutorialproper,andactuallyhacksomecode.
TheTutorial:TheJourneyBegins
YourFirstTime
Assumingallhasgonetoplan,youcannowcreateyourfirstPerlscript.Followtheseinstructions,butbeforeyoustartreadthem
throughonce,thenbegin.That'sagoodideawithanyformofcomputerrelatedprocedure.So,tobegin:
1. Createanewdirectoryforyourperlscripts,separatetoyourdatafilesandtheperlinstallation.Forexample,c:\scripts\,
whichiswhatI'llassumeyouareusinginthistutorial.
2. Startupwhatevertexteditoryou'regoingtohackPerlwith.Notepad.Exeisjustfine.Ifyoucan'tfindNotepadonyourStart
menu,presstheStartbutton,thenselectRun,typein'notepad'andclickOK.
3. TypethefollowinginNotepad
print "My first Perl script\n"
4. Savethetoc:\scripts\myfirst.pl.Becareful!Notepadwillmaysavefileswitha.txtextension,soyouwillendupwith
myfirst.txt.plbydefault.Perlwon'tmind,it'llstillexecutethefile.IfyourversionofNotepaddoesthis,select"Allfiles"
beforesavingorrenamethefilethenloaditagain.Betteryet,useadecenttexteditor!
5. Youdon'tneedtoexitNotepadkeepitopen,aswe'llbemakingchangesverysoon.
6. Switchtoyourcommandprompt.Ifyoudon'tknowhowtostartacommandprompt,click'Start'andthen'Run'.Ifusing
Windows9x,typein'command'andpressenter.IfusingNT,typein'cmd'andpressEnter.
7. Changetoyourperlscriptsdirectory,forexamplecd \scripts .
8. Holdyourbreath,andexecutethescript:perl myfirst.pl
andyou'llseetheoutput.WelcometotheworldofPerl!SeewhatImeanaboutitbeingeasytostart?However,itisdifficultto
finishwithPerlonceyoubegin:)
Whatifitdoesn't...?
Soyoutypedinperl myfirst.plandyoudidn'tseeMy first Perl scriptonthescreen.Ifyousaw"badcommandor
filename"theneitheryouhaven'tinstalledPerlorperl.exeisnotinyourpath.Probablythelatter.Reboot,thentryagain.
IfyousawCan't open perl script "xxxx.pl": No such file or directorythenperlisdefintelyinstalled,butyouhave
eithergotthenameofthescriptwrongorthescriptisnotinthesamedirectoryaswhereyouaretryingtorunitfrom.Forexample,
maybeyousavedinscriptinc:\windowsandyouareinc:\scripts soofcoursePerlcomplainsitcan'tfindthescript.Could
you?Well,don'texpectPerltothen.Youdon'thavetorunthescriptfromthedirectoryinwhichitresides,butitiseasier.
Assumingit'snowallright...
Wneedtoanalysewhat'sgoingonherealittle.Firstnotethatthelineendswithasemicolon .AlmostalllinesofcodeinPerl
havetoendwithsemicolons,andthosethatdon'thavetowillacceptsemicolonsanyway.Themoralisusesemicolons.Sorry
themoralisusesemicolons.
4/10/12 Robert's Perl Tutorial
11/86 www.sthomas.net/roberts-perl-tutorial.htm
Oh,onemorethingifyouhaven'talreadydoneso,continuebreathing.
Alsonotethe\n .ThisisthecodetotellPerltooutputanewline.What'sanewline?Deletethe\n fromtheprogramandrunit
again:
print "My first Perl script"
andallshouldbecomeclear.YouhavenowwrittenyourfirstPerlscript.
Shebang
AlmosteveryPerlbookiswrittenforUN*X,whichisaproblemforWin32.Thisleadstoscriptslike:
#!c:/perl/perl.exe
print "I'm a cool Perl hacker\n"

Thefunctionofthe'shebang'lineistotelltheshellhowtoexecutethefile.UnderUNIX,thismakessense.UnderWin32,the
systemmustalreadyknowhowtoexecutethefilebeforeitisloadedsothelineisnotneeded.
However,thelineisnotcompletelyignored,asitissearchedforanyswitchesyoumayhavegivenPerl(forexample-w toturnon
warnings).
YoumayalsochoosetoaddthelinesoyourscriptsrundirectlyonUNIXwithoutmodification,asUNIXboxesprobablydoneedit.
Win32systemsdonot.Weshallcontinuewiththelesson.
Variables
Scalars
SoPerlisworking,andyouareworkingwithPerl.Nowforsomethingmoreinterestingthansimpleprinting.Variables.Let'stake
simplescalarvariablesfirst.Ascalarvariableisasinglevalue.Like$var=10whichsetsthevariables$var tothevalueof10.
Later,we'lllookatlistslikearraysandhashes,where@var referstomorethanonevalue.Forthemoment,rememberthatScalar
isSingular.Ifweirdmetaphorshelp,thinkoflotsofscalysnakesatasinglesbar.Ifthatdidn'thelp,Iapologiseforputtingthe
thoughtintoyourmind.
$%@areGoodThings
Ifyouhaveanyexperiencewithotherprogramminglanguagesyoumightbesurprisedbythecode$var=10.Withmostlanguages,
ifyouwanttoassignthevalue10 toavariablecalledvar you'dwritevar=10.
NotsoinPerl.ThisisaFeature.Allvariablesareprefixedwithasymbolsuchas$ @ % .Thishascertainadvantages,likemaking
programseasiertoread.Honestly,I'mserious!Itjusttakessomegettingusedto.Theprefixesmeanthatyoucanseewherethe
variablesarequiteeasily.Andnotonlythat,whatsortofvariableitis.ThehumanlanguageGermanhasasimilarprinciple
(exceptnounsarecapitalised,notprefixedwith$ andPerliseasiertopronounce).You'llagreelater,Ithink.
So,everonwards.Timetotrysomemorevariables:
$string="perl"
$num1=20
$num2=10.75
print "The string is $string, number 1 is $num1 and number 2 is $num2\n"

Typing
Acloserlook...noticeyoudon'thavetosaywhattypeofvariableyouaredeclaring.Inotherlanguagesyouneedtosayifthe
variableisastring,array,whatsortofnumberitisandsoon.Youmightevenhavetodeclarewhattypeofnumberitis.Asan
example,inJavayou'dbeensayingthingslikeint var=10whichdefinesthevariablevarasaninteger,withthevalue10.
4/10/12 Robert's Perl Tutorial
12/86 www.sthomas.net/roberts-perl-tutorial.htm
So,whydotheseotherprogramminglanguagesforceyoutodeclareexactlywhatyourvariablesare?Wouldn'titbeeasierifwe
couldjustnotbother?
Forshortprograms,yes.Forreallybigprojectswithmanyprogrammersworkingonthesameapplication,no.That'sbecause
forcingvariabletypedeclarationalsoforcesacertaindisciplineandrigourwhichiswhatyouneedonbigprojects.
Asyouknow,Perlisnotdesignedforgiganticsoftwareengineeringefforts.Itisallaboutsmall,quickprograms.Forthesepurposes
youdon'tneedtherigourofvariablecontrolsasmuch,soPerldoesn'tbother.
Thisideaofforcingaprogrammertodeclarewhatsortofvariableisbeingcreatediscalledtyping.AsPerldoesn'tbydefault
enforceanyrulesontyping,itissaidtobealooselytypedlanguage,asopposedtosomethinglikeC++whichisstronglytyped.
VariableInterpolation
Westillhaven'tfinishedlearningfromthathumblebitofcode.Torefreshyourmemory,hereitisagain:
$string="perl"
$num1=20
$num2=10.75
print "The string is $string, number 1 is $num1 and number 2 is $num2\n"
Noticethewaythevariablesareusedinthestring.Stickingvariablesinsideofstringshasatechnicalterm"variable
interpolation".Now,ifwedidn'thavethehandy$ prefixforwe'dhavetodosomethingliketheexamplebelow,whichis
pseudocode.Pseudocodeiscodetodemonstrateaconcept,notdesignedtoberun.LikecertainMicrosoftsoftware.
print "The string is ".string." and the number is ".num."\n"
whichismuchmorework.Convincedaboutthoseprefixesyet?
Tryrunningthefollowingcode:
$string="perl"
$num=20
print "Doubles: The string is $string and the number is $num\n"
print 'Singles: The string is $string and the number is $num\n'

Doublequotesallowtheaforementionedvariableinterpolation.Singlequotesdonot.Bothhavetheirusesasyouwillseelater,
dependingonwhetheryouwishtointerpolateanything.
ChangingVariables
Auto(de|in)crements
Ifyouwanttoadd1toavariableyoucan,logically,dothis$num=$num+1 .Thereisashorterwaytodothis,whichis$num++.This
isanautoincrement.Guesswhatthisis$num-- .Yes,anautodecrement.
Thisexampleillustratestheabove:
$num=10
print "\$num is $num\n"
$num++
print "\$num is $num\n"
$num--
print "\$num is $num\n"
$num+=3
print "\$num is $num\n"

Thelastexampledemonstratesthatitdoesn'thavetobejust1youcanaddordecreaseby.
4/10/12 Robert's Perl Tutorial
13/86 www.sthomas.net/roberts-perl-tutorial.htm
Escaping
There'ssomethingelsenewinthecodeabove.The\ .Youcanseewhatthisdoesit'escapes'thespecialmeaningof$ .
Escapingmeansthatjustthe$ symbolisprintedinsteadofitreferringtoavariable.
Actually\ hasadeepermeaningitescapesallofPerl'sspecialcharacters,notjust$ .Also,itturnssomenonspecial
charactersintosomethingspecial.Likewhat?Liken .Addthemagic\ andthehumble'n'becomesthemightyNewLine!The\
charactercanalsoescapeitself.Soifyouwanttoprintasingle\ try:
print "the MS-DOS path is c:\\scripts\\"

Oh,'\'isalsousedforotherthingslikereferences.Butthat'snotevencoveredhere.
Thereisatechnicaltermforthese'specialcharacters'suchas@ $ %.Theyarecalledmetacharacters.Perlusesplentyof
metacharacters.Infact,you'llwearyourkeyboardprettyevenlyduringanight'sperlhacking.IthinkitissafetosaythatPerluses
everypossiblekeystrokeandshiftedkeystrokeonastandardUSPCkeyboard.
You'llbeworkingwithallsortsofobscurecharactersinyourPerlhackingcareer,andIalsomeanthoseonyourkeyboard.Thishas
earnedperlareputationforbeingdifficulttounderstand.That'sentirelytrue.Perldoehavesuchareputation,nodoubtaboutit.
Isthereputationjustified?Inmyopinion,Perldoeshaveashortbutsteeplearningcurvetobeginwithsimplybecauseitisso
different.However,onceyoulearnthecharactermeaningsreadingperlcodebecomesmucheasierpreciselybecauseofall
thesestrangecharacters.
Context:AboutPerland@^$%&`/?
Perlusessomanyweirdcharactersthattherearen'tenoughtogoround.Sosometimesthesamecharacterhastwoormore
meanings,dependingonitscontext.Asanexample,thehumbledot. canjointwovariablestogether,actasawildcardor
becomearangeoperatoriftherearetwoofthemtogether.Thecaret^ hasdifferenteffectsin[^abc] asopposedto[a^bc] .
Ifthissoundscrazy,thinkabouttheEnglishlanguage.Whatdothefollowingmeantoyou?
MEAN
POLISH
LIKE
Meanis,inonecontext,isawordtouseddescribethepurposeofsomething.Itisalsoanotherwordforaverage.Furthermore,it
describesanastyperson,orapersonwhodoesn'tlikespendingmoney,andisusedinslangtorefertosomethingimpressiveand
good.
That'sfivedifferentusesfor'mean',andyoudon'thaveanytroubleunderstandingwhichoneImeanduetocontext.
Polish,whencapitalised,caneithermeanpertainingtothecountryPoland,ortheactofmakingsomethingshiny.And'like'can
meansimilarto,oraffectionfor.
So,whenyouspeakorwriteEnglish(thinkoftwo,toandtoo)youknowwhatthesewordsmeanbytheircontext.Itisexactlythe
samewaywithPerl.Justdon'tassumeagivenmetacharacteralwaysmeanswhatyoufirstthoughtitdid.
Tofinishoffthissection,trythefollowing:
StringsandIncrements
$string="perl"
$num=20
$mx=3
print "The string is $string and the number is $num\n"
$num*=$mx
$string++
print "The string is $string and the number is $num\n"
Note the easy shortcut *= meaning 'multiply $num by $mx' or, $num=$num*$mx .
4/10/12 Robert's Perl Tutorial
14/86 www.sthomas.net/roberts-perl-tutorial.htm
Of course Perl supports the usual + - * / ** % operators. The last two are
exponentiation (to the power of) and modulus (remainder of x divided by y).
Also note the way you can increment a string ! Is this language flexible or what ?

Print:AListOperator
Theprint functionisalistoperator.Thatmeansitacceptsalistofthingstoprint,separatedbycommas.Asanexample:
print "a doublequoted string ", $var, 'that was a variable called var', $num," and a newline \n"
Of course, you just put all the above inside a single
doublequoted string:
print "a doublequoted string $var that was a variable called var $num and a newline \n"
to achieve the same effect. The advantage of using the print function in list context
is that expressions are evaluated before being printed. For example, try this:
$var="Perl"
$num=10
print "Two \$nums are $num * 2 and adding one to \$var makes $var++\n"
print "Two \$nums are ", $num * 2," and adding one to \$var makes ", $var++,"\n"

Youmighthavebeenslightlysurprisedbytheresultofthatlastexperiment.Inparticular,whathappenedtoourvariable$var ?It
shouldhavebeenincrementedbyone,resultinginPerm.Thereasonbeingthat'm'isthenextletterafter'l':)
Actually,itaincrementedby1.Wearepostincrementing$var++thevariable,ratherthanpreincrementingit.
Thedifferenceisthatwithpostincrements,thevalueofthevariableisreturned,thentheoperationisperformedonit.Sointhe
exampleabove,thecurrentvalueof$var wasreturnedtotheprint function,then1wasadded.Youcanprovethistoyourself
byaddingthelineprint "\$var is now $var\n" totheendoftheexampleabove.
Ifwewanttheoperationtobeperformedon$varbeforethevalueisreturnedtotheprintfunction,thenpreincrementisthewayto
go.++$varwilldothetrick.
SubroutinesAFirstLook
Let'stakeaanotherlookattheexampleweusedtoshowhowtheautoincrementsystemworks.Messy,isn'tit?ThisisBatchFile
WritingMentality.Noticehowweuseexactlythesamecodefourtimes.Whynotjustputitinasubroutine?
$num=10 # sets $num to 10
&print_results # prints variable $num
$num++
&print_results
$num*=3
&print_results
$num/=3
&print_results
sub print_results {
print "\$num is $num\n"
}

Easierandneater.Thesubroutinecangoanywhereinyourscript,atthebeginning,end,middle...makesnodifference.PersonallyI
putallmineatthebottomandreservethetoppartforsettingvariablesandmainprogramflow.
Asubroutineisjustsomecodeyouwanttousemorethanonceinthesamescript.InPerl,asubroutineisauserdefinedfunction.
Thereisnodifference.ForthepurposesofclarityI'llrefertothemassubroutines.
Asubroutineisdefinedbystartingwithsub thenthename.Afterthatyouneedacurlyleftbracket{ ,thenallthecodeforyour
4/10/12 Robert's Perl Tutorial
15/86 www.sthomas.net/roberts-perl-tutorial.htm
subroutine.Finishitoffwithaclosingbrace} .Theareabetweenthetwobracesiscalledablock.Rememberthis.Thereare
suchthingsasanonymoussubroutinesbutnothere.Everythingherehasaname.
Subroutinesareusuallycalledbyprefixingtheirnamewithanampersand,thatisoneofthese& ,likeso&print_results .It
usedtobecooltoomitthe& prefixbutallperlhackersarenowencouragedtouseittoavoidambiguity.Ambiguitycanhurtyouif
youdon'tavoidit.
Ifyouareworryingaboutvariablevisibility,don't.Allthevariablesweareusingsofararevisibleeverywhere.Youcanrestrict
visibilityquiteeasily,butthat'snotimportantrightnow.Ifyouweren'tworryingaboutvariablevisibility,pleasedon'tstart.I'dtellyou
it'snotimportantbutthat'llonlymakeyouworried.(paranoid?)We'llcoveritlater.
Comments
Didyouseea# creptinthere.That'sacomment.Everythingaftera# isignored.Youcan'tcontinueitontoanewlinehowever,so
ifyourcommentwon'tfitononelinestartanewonewith# .TherearewaystocreatePlainOldDocumentation(POD)andmore
waystocommentbuttheyarenotdetailedhere.
Comparisons
Aniffystart
Anif statementissimple.if the day is Sunday, then lie in bed.Asimpletest,withtwooutcomes.Perlconversion(don't
runthis):
if ($day eq "sunday") {
&lie_in_bed
}
Youalreadyknowthat&lie_in_bed isacalltoasubroutine.Weassume$day issetearlierintheprogram.If$day isnotequal
to'Sunday'&lie_in_bed isnotexecuted(pity).Youdon'tneedtosayanythingelse.Trythis:
$day="sunday"
if ($day eq "sunday") {
print "Zzzzz....\n"
}
Note the syntax. The if statement requires something to test for Truth. This expression must
be in (parens), then you have the braces to form a block.

TheTruthAccordingtoPerl
TherearemanyPerlfunctionswhichtestforTruth.Someareif, while, unless .Soitisimportantyouknowwhattruthis,as
definedbyPerl,notyourtaxforms.Therearethreemainrules:
1. Anystringistrueexceptfor""and"0".
2. Anynumberistrueexceptfor0.Thisincludesnegativenumbers.
3. Anyundefinedvariableisfalse.Aundefinedvariableisonewhichdoesn'thaveavalue,iehasnotbeenassignedto.
Someexamplecodetoillustratethepoint:
&isit # $test1 is at this moment undefined
$test1="hello" # a string, not equal to "" or "0"
&isit
$test1=0.0 # $test1 is now a number, effectively 0
&isit
4/10/12 Robert's Perl Tutorial
16/86 www.sthomas.net/roberts-perl-tutorial.htm
$test1="0.0" # $test1 is a string, but NOT effectively 0 !
&isit
sub isit {
if ($test1) { # tests $test1 for truth or not
print "$test1 is true\n"
} else { # else statement if it is not true
print "$test1 is false\n"
}
}
The first test fails because $test1 is undefined. This means it has not been created by
assigning a value to it. So according to Rule 3 it is false. The last two tests are
interesting. Of course, 0.0 is the same as 0 in a nmeric context. But it is no the
same as 0 in a string context, so in that case it is true.
Soherewearetestingsinglevariables.What'smoreusefulistestingtheresultofanexpression.Forexample,thisisan
expression$x * 2 andsoisthis$var1 + $var2 .Itistheendresultoftheseexpressionsthatisevaluatedfortruth.
Anexampledemonstratesthepoint:
$x=5
$y=5
if ($x - $y) {
print '$x - $y is ',$x-$y," which is true\n"
} else {
print '$x - $y is ',$x-$y," which is false\n"
}

Thetestfailsbecause55ofcourseis0,whichisfalse.Theprint statementmightlookalittlestrange.Rememberthatprint is
alistoperator?Sowehanditalist.Firstitem,asinglequotedstring.Itissinglequotedbecauseitwedonotwanttoperform
variableinterpolationonit.Nextitemisanexpressionwhichisevaluated,andtheresultprinted.Finally,adoublequotedstringis
usedbecausewewanttoprintanewline,andwithoutthedoublequotesthe\n won'tbeinterpolated.
Whatisprobablymoreusefulthantestingaspecificvariablefortruthisequalitytesting.Forexample,hasyourluckynumberbeen
drawn?
$lucky=15
$drawnum=15
if ($lucky == $drawnum) {
print "Congratulations!\n"
} else {
print "Guess who hasn't won!\n"
}

Theimportantpointabouttheabovecodeistheequalityoperator,== .
EqualityandPerl
Nowpaycloseattention,otherwiseyou'llenduppostinganannoyingquestionsomewhere.ThisisaFAQ,asinaFrequentlyAsked
Question.
Thesymbol= isanassignmentoperator,noacomparisonoperator.Therefore:
if ($x = 10)isalwaystrue,because$x hasbeenaignedthevalue10successfully.
if ($x == 10) compaethetwovalues,whichmightnotbeequal.
Sofarwehavebeentestingnumbers,butthereismoretolifethannumbers.Therearestringstoo,andtheseneedtestingtoo.
$name = 'Mark'
$goodguy = 'Tony'
4/10/12 Robert's Perl Tutorial
17/86 www.sthomas.net/roberts-perl-tutorial.htm
if ($name == $goodguy) {
print "Hello, Sir.\n"
} else {
print "Begone, evil peon!\n"
}

Somethingseemstohavegonewronghere.ObviouslyMarkisdifferenttoTony,sowhydoesperlconsiderthemequal?
MarkandTonyaeequalnumerically.Weshouldbetestingthemasstrings,notasnumbers.Todothis,simplysubstitute== for
eq andeverythingwillworkasexpected.
AllEqualityisNotEqual:NumericversusString
Therearetwotypesofcomparisonoperatornumericandstring.You'vealreadyseentwo,== andeq.Runthis:
$foo=291
$bar=30
if ($foo < $bar) {
print "$foo is less than $bar (numeric)\n"
}
if ($foo lt $bar) {
print "$foo is less than $bar (string)\n"
}

Theltoperatorcomparesinastringcontext,andofcourse< comparesinanumericcontext.
Alphabetically,thatisinastringcontext,291comesbefore30.ItisactuallydecidedbytheASCIIvalue,butalphabeticallyisclose
enough.Changethenumbersaroundalittle.NoticehowPerldoesn'tcarewhetheritusesastringcomparisonoperatorona
numericvalue,orviceversa.ThisistypicalofPerl'sflexibility.
BondageanddisciplineareprettymuchalienconceptstoPerl(andtheauthor).Thisflexibilitydoeshaveadrawback.Ifyou'reona
programmingprecipice,threateningsuicidebyjumpingoff,Perlwon'ttalkyououtofyourdecisionbutwillprovideseveralwaysof
jumping,steppingorfallingtoyourdoomwhilesilentlywatchingyourearlyconclusion.Sobecareful.
AninterludeThePerlMotto
ThePerlMottois"ThereisMoreThanOneWaytoDoIt"orTIMTOWTDI.Pronounced'TimToady'.Thistutorialdoesn'ttry
andmentionallpossiblewaysofdoingeverything,mainlybecausetheauthorisfartoolazy.WriteyourPerlprogramsthewayyou
wantto.
TheComparisonOperatorsListed
Therestoftheoperatorsare:
Compaion Nmeic Sing
Equal == eq
Notequal != ne
Greaterthan > gt
Lessthan < lt
Greaterthanorequalto >= ge
Lessthanorequalto <= le
4/10/12 Robert's Perl Tutorial
18/86 www.sthomas.net/roberts-perl-tutorial.htm
TheGoldenRuleofComparisons
Theymaybeodious,butrememberthefollowing:
ifyouaretestingavalueasastringthereshouldbeonlylettersinyourcomparisonoperator.
ifyouaretestingavalueasanumberthereshouldonlybenonalphacharactersinyourcomparisonoperator
note'asa'above.Youcantestnumbersasstringandviceversa.Perlnevercomplains.
MoreAboutIf:Multiples
Moreaboutif statements.Runthis:
$age=25
$max=30
if ($age > $max) {
print "Too old !\n"
} else {
print "Young person !\n"
}
It is easy to see what else does. If the expression is false then whatever is in
the else block is evaluated (or carried out, executed, whatever term you choose to use).
Simple. But what if you want another test ? Perl can do that too.

elsif
$age=25
$max=30
$min=18
if ($age > $max) {
print "Too old !\n"
} elsif ($age < $min) {
print "Too young !\n"
} else {
print "Just right !\n"
}
If the first test fails, the second is evaluated. This carries on until there are no
more elsif statements, or an else statement is reached. An else statement is optional,
and no elsif statements should come after it. Logical, really.
Thereisabigdifferencebetweentheaboveexampletheonebelow:
if ($age > $max) {
print "Too old !\n"
}
if ($age < $min) {
print "Too young !\n"
}

Ifyourunit,itwillreturnthesameresultinthiscase.However,itisBadProgrammingPractice.Inthiscasewearetestinga
number,butsupposeweweretestingastringtoseeifitcontainedRorS.ItispossiblethatastringcouldcontainbohRandS.So
itwouldpassboth'if'tests.Usinganelsif avoidsthis.Assoonasthefirststatementistrue,nomoreelsif statements(andno
else statement)areexecuted.
Youdon'tneedtotakeupawholethreelines:
print "Too old\n" if $age > $max
print "Too old\n" unless $age < $max
4/10/12 Robert's Perl Tutorial
19/86 www.sthomas.net/roberts-perl-tutorial.htm

Iaddedsomewhitespacethereforaestheticbeauty.Thereareotheroperatorsthatyoucanuseinsteadofif andunless ,but
that'sforlateron.
Incidentally,thetwolinesofcodeabovedonotdoexactlythesamething.Consideramaximumageof50andinputageof50.
Therefore,youshouldbeverycarefulaboutyourlogicwhenwritingcode(niceobviousstatementthere).
Forthosethatwerewondering,Perlhasnocasestatement.ThisisallexplainedintheFAQ,whichislocatedat
http://www.perl.com/.
UserInput
STDINandotherfilehandles
Sometimesyouhavetointeractwiththeuser.Itisapain,butsometimesnecessary,especiallyfortheliveones.Toaskforinput
anddosomethingwithittrythis:
print "Please tell me your name: "
$name=<STDIN>
print "Thanks for making me happy, $name !\n"
New things to learn here. Firstly, <STDIN> . STDIN is a filehandle. Filehandles are what
you use to interact with things such as files, console input, socket connections and more.
YoucouldsaySTDINisthestandardsourceforinput.GuesswhatSTDINstandsfor.InthiscasetheSTDINfilehandleisreading
fromtheconsole.
Theanglebrackets<> readdatafromafilehandle.Exactlyhowmuchisdependentonwhatyoudo,butinthiscaseitiswhatever
wasinputattheprompt.
SowearereadingfromtheSTDINfilehandle.Thevalueisassignedto$name andprinted.Anyideawhythe!endsuponanew
line?onanelineonanewline????
AsyoupressedEnter,youofcourseincludedanewlinewithyourname.Theeasywaytogetridofitistochop itoff:
Chop
print "Please tell me your name: "
$name=<STDIN>
chop $name
print "Thanks for making me happy, $name !\n"
and that fails with a syntax error. Can you spot why? Look at the error code, look at the
line number and see where the syntax is wrong. The answer is a missing semicolon
( ) on the end of the last two lines.
Ifyouadda totheendofline3,butnottothelastline,thentheprogramworksasitshould.ThisisbecausePerldoesn'tneeda
semicolontoendthelaststatementofablock.However,I'dadviseendingallyourstatementswithsemicolonsbecauseyoumay
wellbeaddingmorecodetothemanditisonlyonelittlekeystroke.
Whenyouaddthesemicolon(s),theprogramrunscorrectly.Thechop functionremovesthelastcharacterofwhateveritisgiven
tochop,inthiscaseremovingthenewlineforus.Infact,thatcanbeshortened:
print "Please tell me your name: "
chop ($name=<STDIN>)
print "Thanks for making me happy, $name !"

Theparentheses( ) forcechop toactontheresultofwhatisinsidethem.So$name=<STDIN> isevaluatedfirst,thentheresult
fromthat,whichis$name ,ischopped.Tryitwithout.
YoucanreadfromSTDINasmuchasyoulike.ForyourentertainmentIhavecreatedasophisticatedmultinationalgreeting
4/10/12 Robert's Perl Tutorial
20/86 www.sthomas.net/roberts-perl-tutorial.htm
machine:
print "Please tell me your name: "
chop ($name=<STDIN>)
print "Please tell me your nationality: "
chop ($nation=<STDIN>)
if ($nation eq "British" or $nation eq "New Zealand") {
print "Hallo $name, pleased to meet you!\n"
} elsif ($nation eq "Dutch" or $nation eq "Flemish") {
print "Hoi $name, hoe gaat het met u vandaag?!\n"
} else {
print "HELLO!!! SPEAKEEE ENGLIEESH???\n"
}

AsidefromdemonstratingthenativeEnglishspeaker'slinguistictalents,thisscriptalsointroducestheor logicaloperator.We'll
coveror anditsassociatesinmoredetaillateron.First,awordofwarning.
Choppingisdangerous,asmyfriendOneHandHaroldwilltellyou.Everyoneisconcernedaboutvariousformsofsafetythese
days,andyourperlcodeshouldbenoexception.
SafeChoppingwithChomp
Ratherthanjustwantonlyremovethelastcharacterregardlessofwhateveritis,withoutacareintheworld,justsimplyconsigning
thepoorlittlethingtotheGreatBitBucketintheSky,youcanremovethelastcharacteronlyifitisanewlinewithchomp :
chomp ($name=<STDIN>)

Atthispointtheperlgurusarescreaming"Ifoundanerror!".Well,chomp doesn'talwaysremovethelastcharacterifitisanewline
butifitdoesn't,youhavesetaspecialvariable,namely$/ ,tosomethingdifferent.Ipresumethatifyoudoset$/ youknowwhat
itdoes.Itisexplainedlaterinthisverydocument.Ofcourse,beingagoodpupil,youwouldn'texperimentwiththeunknown,blindly
changingthingsjustforthehellofittoseewhathappens.
Ifyoudon't,you'llneverlearnanythinguseful.
Arrays
Lists,herdswhatarearrays?
Perlhastwotypesofarray,associativearrays(hashes)andarrays.Bothtypesarelists.Alistisjustacollectionofvariables
referredtoasthecollection,notasindividualelements.
YoucanthinkofPerl'slistsasaherdofanimals.Listcontextreferstotheentireherd,scalarcontextreferstoasingleelement.A
listisaherdofvariables.Thevariablesdon'thavetobeallofthesametypeyoumighthaveaherdoftensheep,threelionsand
twowolves.Itwouldprobablybejustthreelionsandonewolfbeforelong,butbearwithme.Inthesameway,youmighthavea
Perllistofthreescalarvariables,twoarrayelementsandtenhashelements.
Certaintypesoflistsareknownbycertainnames.Justasaherdofsheepiscalledaflock,aherdoflionsiscalledapride,aherd
ofwolvesiscalledapackandaherdofmanagersaconfusion,sometypesofPerllisthaveaspecialnames.
BasicArrayWork
Forexample,anarrayisanorderedlistofscalarvariables.Thislistcanbereferredtoasawhole,oryoucanrefertoindividual
elementsinthelist.Theprogrambelowdefinesaanarray,called@names .Itputsfivevaluesintothearray.
4/10/12 Robert's Perl Tutorial
21/86 www.sthomas.net/roberts-perl-tutorial.htm
@names=("Muriel","Gavin","Susanne","Sarah","Anna")
print "The elements of \@names are @names\n"
print "The first element is $names[0] \n"
print "The third element is $names[2] \n"
print 'There are ',scalar(@names)," elements in the array\n"
Firstly,noticehowwedefine@names .Asitisinalistcontext,weareusingparens.Eachvalueiscommaseparated,whichis
Perl'sdefaultlistdelimiter.Thedoublequotesarenotnecessary,butasthesearestringvaluesitmakesiteasiertoreadand
changelateron.
Next,noticehowweprintit.Simplyrefertoitasawhole,thatisinlistcontext..Listcontextmeansreferringtomorethanone
elementofalistatatime.Thecodeprint @names willworkperfectlywelltoo.But....
IusuallylearnsomethingaboutPerleverytimeIworkwithit.Whenrunningacourse,astudenttaughtmethistrickwhichhehad
discovered:
@names=("Muriel","Gavin","Susanne","Sarah","Anna","Paul","Trish","Simon")
print @names
print "\n"
print "@names"
When a list is placed inside doublequotes, it is space delimited when interpolated. Useful.
Ifwewanttodoanythingwiththearrayasalist,thatisdoingsomethingwithmorethanonevalue,thenrefertothearrayas
@array .That'simportant.The@ prefixisusedwhenyouwanttorefertomorethanoneelementofalist.
Whenyourefertomorethanone,butnotallelementsofanarraythatisknownasaslice.Cakeanalogiesareappropriate.Pie
analogiesareprobablyhealthierbutequallyaccurate.
ElementsofArrays
Arraysarenotmuchuseunlesswecangettoindividualelements.Firstly,wearedealingwithasingleelementofthelist,sowe
cannotuse@ whichreferstomultipleelementsofthearray.Itisasingle,scalarvariable,so$isused.Secondly,wemust
specifywhichelementwewant.That'seasy$array[0] forthefirst,$array[1] forthesecondandsoforth.Arrayindexesstart
at0,unlessyoudosomethingwhichissohighlydeprecated('deprecated'meansallowed,usuallyforbackwardscompatibility,but
disapprovedofbecausetherearebetterways)I'mnotevengoingtomentionit.
Finally,weforcewhatisnormallylistcontext(morethanoneelement)intoscalarcontext(singleelement)togiveustheamountof
elementsinthearray.Withoutthescalar ,itwouldbethesameasthesecondlineoftheprogram.
Howtorefertoelementsofanarray
Pleaseunderstandthis:
$myvar="scalar variable"
@myvar=("one","element","of","an","array","called","myvar")
print $myvar # refers to the contents of a scalar variable called myvar
print $myvar[1] # refers to the second element of the array myvar
print @myvar # refers to all the elements of array myvar

Thetwovariables$myvar and@myvar arenot,inanyway,related.Notevendistantly.Technically,theyareindifferent
namespaces.
Goingbacktotheanimalanalogy,itislikehavingadognamed'Myvar'andagoldfishcalled'Myvar'.You'llnevergetthetwomixed
upbecausewhenyoucall'Myvar!!!!'oropenacanofdogfoodthe'Myvar'dogwillcomerunningandgoldfishwon't.Now,you
couldn'thavetwodogscalled'Myvar'andinthesamewayyoucan'thavetwoPerlvariablesinthesamenamespacecalled'Myvar'.
4/10/12 Robert's Perl Tutorial
22/86 www.sthomas.net/roberts-perl-tutorial.htm
Morewaystoaccessarrays
Theelementnumbercanbeavariable.
print "Enter a number :"
chomp ($x=<STDIN>)
@names=("Muriel","Gavin","Susanne","Sarah","Anna")
print "You requested element $x who is $names[$x]\n"
print "The index number of the last element is $#names \n"
This is useful. Notice the last line of the example. It returns the index number of
the last element. Of course you could always just do this $last=scalar(@names)-1 but
this is more efficient. It is an easy way to get the last element, as follows:
print "Enter the number of the element you wish to view :"
chomp ($x=<STDIN>)
@names=("Muriel","Gavin","Susanne","Sarah","Anna","Paul","Trish","Simon")
print "The first two elements are @names[0,1]\n"
print "The first three elements are @names[0..2]\n"
print "You requested element $x who is $names[$x-1]\n" # starts at 0
print "The elements before and after are : @names[$x-2,$x]\n"
print "The first, second, third and fifth elements are @names[0..2,4]\n"
print "a) The last element is $names[$#names]\n" # one way
print "b) The last element is @names[-1]\n" # different way
It looks complex, but it is not. Really. Notice you can have multiple values separated
by a comma. As many as you like, in whatever order. The range operator .. gives you
everything between and including the values. And finally look at how we print the last
element - remember $#names gives us a number ? Simply enclose it inside square brackets
and you have the last element.
Doalsonotethatbecauseelementaccessessuchas[0,1] aremorethanonevariable,wecannotusethescalarprefix,namely
the$ symbol.Weareaccessingthearrayinlistcontext,[email protected]'tmatterthatitisnottheentirearray.
Remember,accessingmorethanoneelementofanarraybutnottheentirearrayiscalledaslice.Iwon'tgooverthefood
analogiesagain.
ForLoops
AforLoopdemonstrated
Allwellandgood,butwhatifwewanttoloadeachelementofthearrayinturn?Well,wecouldbuildaforlooplikethis:
@names=("Muriel","Gavin","Susanne","Sarah","Anna","Paul","Trish","Simon")
for ($x=0 $x <= $#names $x++) {
print "$names[$x]\n"
}
which sets $x to 0, runs the loop once, then adds one to $x , checks it is less than
$#names , if so carries on. By the way, that was your introduction to for loops. Just
to go into a little detail there, the for loop has three parts to it:
Initialisation
TestCondition
Modification
Inthiscase,thevariable$x isinitialisedto0.Itisimmediatelytestedtoseeifitissmallerthan,orequalto$#names .Ifthatistrue,
4/10/12 Robert's Perl Tutorial
23/86 www.sthomas.net/roberts-perl-tutorial.htm
thentheblockisexecutedonce.Critically,ifitisnotruetheblockisnoexecutedatall.
Oncetheblockhasbeenexecuted,themodificationexpressionisevaluated.That's$x++ .Then,thetestconditionischeckedto
seeiftheblockshouldbeexecutedornot.
Forloopswith..,therangeoperator
Thereisaanotherversion:
for $x (0 .. $#names) {
print "$names[$x]\n"
}
which takes advantage of the range operator .. (two dots together). This simply gives $x the
value of 0, then increments $x by 1 until it is equal to $#names .

foreach
Fortruebeautywemustuseforeach .
foreach $person (@names) {
print "$person"
}
This goes through each element ('iterates', another good technical word to use)
of @names , and assigns each element in turn to the variable $person . Then you can do
what you like with the variable. Much easier. You can use
for $person (@names) {
print "$person"
}
if you want. Makes no difference at all, aside from a little clarity.

Theinfamous$_
Infact,thatgetsshorter.AndnowIneedtointroduceyouto$_ ,whichistheDefaultInputandPatternSearchingVariable.
foreach (@names) {
print "$_"
}
If you don't specify a variable to put each element into, $_ is used instead as it is
the default for this operation, and many, many others in Perl. Including the print function :
foreach (@names) {
print
}
As we haven't supplied any arguments to print , $_ is printed as default. You'll be seeing
a lot of $_ in Perl. Actually, that statement is not exactly true. You will be seeing lot
of places where $_ is used, but quite often when it is used, it is not actually written.
In the above example, you don't actually see $_ but you know it is there.

APrematureEndtoyourloop
Aloop,byitsnature,continues.Ifthatdidn'tmakesense,startreadingthissentenceagain.
Theoldjokesarethebest,aren'tthey?
Thejokeaboveisaloop.YoucontinuerereadingthesentenceuntilyourealiseI'mtryingtobefunny.Thenyouexittheloop.Or
maybesomebodydoesn'texitit.Whatever,loopsalwaysrununtiltheexpressiontheyaretestingreturnsfalse.Inthecaseofthe
examplesabove,afalsevalueisreturnedwhenalltheelementsofthearrayhavebeencycledthrough,andtheloopends.
Ifyouwantaneverlastingloop,justtestanconditionyouknowwillalwaysbetrue:
4/10/12 Robert's Perl Tutorial
24/86 www.sthomas.net/roberts-perl-tutorial.htm
while (1) {
$x++
print "$x: Did you know you can press CTRL-C to interrupt a perl program?\n"
}
Another way to exit a loop is a simple foreach over the elements, as we have seen. But if we
don't know when we want to exit a loop? For example, suppose we want to print out a list of
names but stop when we find one with a particular title? You are throwing a huge party,
someone is allergic to vodka, and this person has drunk from the punch bowl despite being
assured by someone holding two empty bottles of Absolut that he was just using the bottles
to convey yet more orange juice into said punch bowl. So you need a doctor, and so you write
a Perl script to find one from the list of attendees, wanting the doctor's name to be the last
item printed:
@names=('Mrs Smith','Mr Jones','Ms Samuel','Dr Jansen','Sir Philip')
foreach $person (@names) {
print "$person\n"
last if $person=~/Dr /
}
The last operator is our friend. Don't worry about the /Dr / business -- that is a regular
expression which we cover next. All you need to know is that it returns true if the name begins
with 'Dr '. When it does return true, last is operated and the loop ends early.
Alittlemorecontrolovertheprematureending:Labels
Sothat'seasyenough.Butwait!Weneedamedical,humanfixertypedoctor,notjustanyonewithaPhD.So,thesameprinciple
appliesinthisexamplehere:
@names =('Mrs Smith','Mr Jones','Ms Samuel','Dr Jansen','Sir Philip')
@medics =('Dr Black','Dr Waymour','Dr Jansen','Dr Pettle')
foreach $person (@names) {
print "$person\n"
if ($person=~/Dr /) {
foreach $doc (@medics) {
print "\t$doc\n"
last if $doc eq $person
}
}
}
Aside from showing one way to indent your code, this also demonstrates a nested loop. A nested
loop is a loop within a loop. What happens is that the @names array is searched for a 'Dr ',
and if it is found then the @medics array is searched to make sure the doctor is a human-fixing
doctor not a professor of physics or something. The regular expression has been shifted into
an if statement, where it works nicely as it only returns true or false.
Theproblemwiththecodeisthatafterwefindourmedicaldoctorwewantittostop.Butitdoesn't.Itonlystopstheloopitisin,so
DrPettlenevergetsprinted.However,thecodejustcarriesonwithSirPhilipwhoisterriblysorryoldchap,butcan'tbeofanybally
useatall,whatho!Whatweneedisawaytobreakoutoftheentireloopfromwithinanest.Likeso:
@names =('Mrs Smith','Mr Jones','Ms Samuel','Dr Jansen','Sir Philip')
@medics =('Dr Black','Dr Waymour','Dr Jansen','Dr Pettle')
LBL: foreach $person (@names) {
print "$person\n"
if ($person=~/Dr /) {
foreach $doc (@medics) {
print "\t$doc\n"
last LBL if $doc eq $person
}
}
}
4/10/12 Robert's Perl Tutorial
25/86 www.sthomas.net/roberts-perl-tutorial.htm
Only two changes here. We have defined a label, namely LBL. Instead of breaking out from
the current loop, which is the default, we specify a label to break out to, which is in
the outer loop. This works with as many nested loops as your brain can handle. You don't have
to use uppercase names but for namespace reasons it is recommended, and you can call your
labels whatever you please. I was just being unimaginative with the name of LBL, feel free
to invent labels called DORIS or MATILDA if that's what floats your personal boat.
ChangingtheElementsofanArray
Sowehave@names .Wewanttochangeit.Runthis:
print "Enter a name :"
chomp ($x=<STDIN>)
@names=("Muriel","Gavin","Susanne","Sarah")
print "@names\n"
push (@names, $x)
print "@names\n"
Fairly self explanatory. The push function just adds a value on to the end of the array.
Of course, Perl being Perl, it doesn't have to be just the one value:
print "Enter a name :"
chop ($x=<STDIN>)
@names=("Muriel","Gavin","Susanne","Sarah")
@cities=("Brussels","Hamburg","London","Breda")
print "@names\n"
push (@names, $x, 10, @cities[2..4])
print "@names\n"
This is worth looking at in more detail. It appears there is no fifth element of
@cities , as referred to by @cities[2..4] .
Actually,thereisafifthelement.Addthistotheendoftheexample:
print "There are ",scalar(@names)," elements in \@names\n"
There appear to be 8 elements in @names . However, we have just proved there are in fact 9.
The reason there are 9 is that we referred to non-existent elements of @cities , and Perl
has quite happily extended @names to suit. The array @cities remains unchanged. Try poping
the array if you don't believe me.
Sothat'spush .Nowforsome...
JiggerypokerywithArrays
@names=("Muriel","Gavin","Susanne","Sarah")
@cities=("Brussels","Hamburg","London","Breda")
&look
$last=pop(@names)
unshift (@cities, $last)
&look
4/10/12 Robert's Perl Tutorial
26/86 www.sthomas.net/roberts-perl-tutorial.htm
sub look {
print "Names : @names\n"
print "Cities: @cities\n"
}
Now we have two arrays. The pop function removes the last element of an array and returns it,
which means you can do something like assign the returned value to a variable.
The unshift function adds a value to the beginning of the array. Hope you didn't forget that
&subroutinename calls a subroutine. Presented below are the functions you can use to work with arrays:

Atableofarrayhackingfunctions
push Addsvaluetotheendofthearray
pop Removesandreturnsvaluefromendofarray
shift Removesandreturnsvaluefrombeginningofarray
unshift Addsvaluetothebeginningofarray
Now,accessingotherelementsofarrays.MayIpresentthesplice function?
Splice
@names=("Muriel","Sarah","Susanne","Gavin")
&look
@middle=splice (@names, 1, 2)
&look
sub look {
print "Names : @names\n"
print "The Splice Girls are: @middle\n"
}
The first argument for splice is an array. Then second is the offset. The offset is the index
number of the list element to begin splicing at. In this case it is 1. Then
comes the number of elements to remove, which is sensibly 1 or more in this
case. You can set it to 0 and perl, in true perl style, won't complain.
Setting to 0 is handy because splice can add elements to the middle of an array, and if you don't
want any deleted 0 is the number to use. Like so:
@names=("Muriel","Gavin","Susanne","Sarah")
@cities=("Brussels","Hamburg","London","Breda")
&look
splice (@names, 1, 0, @cities[1..3])
&look
sub look {
print "Names : @names\n"
print "Cities: @cities\n"
}
Notice how the assignment to @middle
has gone -- it is no longer relevant.
Ifyouassigntheresultofasplice toascalarthen:
@names=("Muriel","Sarah","Susanne","Gavin")
&look
4/10/12 Robert's Perl Tutorial
27/86 www.sthomas.net/roberts-perl-tutorial.htm
$middle=splice (@names, 1, 2)
&look
sub look {
print "Names : @names\n"
print "The Splice Girls are: $middle\n"
}
then the scalar is assigned the last element removed, or undef if
it doesn't work at all.
Thesplice functionisalsoawaytodeleteelementsfromanarray.Infact,adiscussionof:
DeletingVariables
isinorder.SupposewewanttodeleteHamburgfromthefollowingarray.Howdowedoit?Perhaps:
@cities=("Brussels","Hamburg","London","Breda")
&look
$cities[1]=""
&look
sub look {
print "Cities: ",scalar(@cities), ": @cities\n"
}

wouldbeappropriate.CertainlyHamburgisremoved.Shame,suchagreatlake.Butnote,thearrayelementstillexists.Thereare
[email protected] function,whichremovestheelemententirely.
splice (@cities, 1, 1)

Nowthat'sallwellandgoodforarrays.Whataboutordinaryvariables,suchasthese:
$car ="Porsche 911"
$aircraft="G-BBNX"
&look
$car=""
&look
sub look {
print "Car :$car: Aircraft:$aircraft:\n"
print "Aircraft exists !\n" if $aircraft
print "Car exists !\n" if $car
}

Itlookslikewehavedeletedthe$car variable.Pity.Butthinkaboutit.Itisnotdeleted,itisjustsettothenullstring"".Asyourecall
(hopefully)frompreviousramblings,thenullstringevaluatestofalsesotheif testfails.
FalsevaluesversusExistence:Itis,therefore...
Justbecausesomethingisfalsedoesn'tmeantosayitdoesn'texist.Awigisfalsehair,butawigexists.Yourvariableisstillthere.
Perldoeshaveafunctiontotestifsomethingexists.Existence,inPerlterms,meansdefined.So:
print "Car is defined !\n" if defined $car

4/10/12 Robert's Perl Tutorial
28/86 www.sthomas.net/roberts-perl-tutorial.htm
willevaluatetotrue,asthe$car variabledoesinfactexist.
Thisbegsthequestionofhowtoreallywipevariablesfromthefaceoftheearth,oratleastyourPerlscript.Simple.
$car ="Porsche 911"
$aircraft="G-BBNX"
&look
undef $car # this undefines $car
&look
sub look {
print "Car :$car: Aircraft:$aircraft:\n"
print "Aircraft exists !\n" if $aircraft
print "Car exists !\n" if defined $car
}

Thisvariable$cariseradicated,deleted,killed,destroyed.
Andnowforsomethingcompletelydifferent....
BasicRegularExpressions
Anintroduction
Orregexforshort.Thesecanbealittleintimidating.ButI'llbetyouhavealreadyusedsomeregexinyourcomputinglifesofar.
Haveyouevensaid"I'llhaveanyDutchbeer?"That'saregexwhichwillmatchaGrolschorHeineken,butnotaBudweiser,
orangejuiceorcheesetoastie.Whataboutdir *.txt ?That'saregularexpressiontoo,listinganyfilesendingin.txt.
Perl'sregexoftenlooklikethis:
$name=~/piper/

Thatissaying"If'piper'isinside$name,thenTrue."
Theregularexpressionitselfisbetween/ / slashes,andthe=~ operatorassignsthetargetforthesearch.
Anexampleiscalledfor.Runthis,andansweritwith'thefaq'.Thentry'mytealeaves'andseewhathappens.
print "What do you read before joining any Perl discussion ? "
chomp ($_=<STDIN>)
print "Your answer was : $_\n"
if ($_=~/the faq/) {
print "Right ! Join up !\n"
} else {
print "Begone, vile creature !\n"
}
So here $_ is searched
for 'the faq'. Guess what we don't need ! The =~ .
This works just as well:
if (/the faq/) {
because if you don't specify a variable, then perl searches $_ by default.
In this particular case, it would be better to use
if ($_ eq "the faq") { as we are testing for exact matches.
4/10/12 Robert's Perl Tutorial
29/86 www.sthomas.net/roberts-perl-tutorial.htm

Senstivityregexesintouchwiththeirinnerchild
Butwhatifsomeoneenters'TheFAQ'?Itfails,becausetheregexiscasesensitive.Wecaneasilyfixthat:
if (/the faq/i) {
with the /i switch, which
specifies case-insensitivity. Now it works for all variations, such as "the
Faq" and "the FAQ".
Nowyoucanappreciatewhyaregularexpressionisbetterinthissituationthanasimpletestusingeq .Astheregexsearchesone
stringforanotherstring,aresponseof"IwouldreadtheFAQfirst!"willalsowork,because"theFAQ"willmatchtheregex.
Studythisexamplejusttoclarifytheabove.Tabsandspaceshavebeenaddedforaestheticbeauty:
$_="perl for Win32" # sets the string to be searched
if ($_=~/perl/) { print "Found perl\n" } # is 'perl' inside $_ ? $_ is "perl for Win32".
if (/perl/) { print "Found perl\n" } # same as the regex above. Don't need the =~ as we are testing $_
if (/PeRl/) { print "Found PeRl\n" } # this will fail because of case sensitivity
if (/er/) { print "Found er\n" } # this will work, because there is an 'er' in 'perl'
if (/n3/) { print "Found n3\n" } # this will work, because there is an 'n3' in 'Win32'
if (/win32/) { print "Found win32\n" } # this will fail because of case sensitivity
if (/win32/i) { print "Found win32 (i)\n" } # this will *work* because of case insensitivity (note the /i)
print "Found!\n" if / / # another way of doing it, this time looking for a space
print "Found!!\n" unless $_!~/ / # both these are the same, but reversing the logic with unless and !
print "Found!!\n" unless !/ / # don't do this, it will always never not confuse nobody :-)
# the ~ stays the same, but = is changed to ! (negation)
$find=32 # Create some variables to search for
$find2=" for " # some spaces in the variable too
if (/$find/) { print "Found '$find'\n" } # you can search for variables like numbers
if (/$find2/) { print "Found '$find2'\n" } # and of course strings !
print "Found $find2\n" if /$find2/ # different way to do the above
As you can see from the last example, you can embed a variable in
the regex too. Regular expressions could fill entire books (and they have
done, see the book critiques at http://www.perl.com/) but here are some useful
tricks:

CharacterClasses
@names=qw(Karlson Carleon Karla Carla Karin Carina Needanotherword)
foreach (@names) { # sets each element of @names to $_ in turn
if (/[KC]arl/) { # this line will be changed a few times in the examples below
print "Match ! $_\n"
} else {
print "Sorry. $_\n"
}
}
This time @names is
initialised using whitespace as a delimiter instead of a comma. qw refers to
'quote words', which means split the list by words. A word ends with whitespace
(like tabs, spaces, newlines etc).
Thesquarebracketsenclosesinglecharacterstobematched.HereeitherKarl orCarl mustbeineachelement.Itdoesn't
havetobetwocharacters,andyoucanusemorethanoneset.ChangeLine4intheaboveprogramto:
4/10/12 Robert's Perl Tutorial
30/86 www.sthomas.net/roberts-perl-tutorial.htm
if (/[KCZ]arl[sa]/) {

matchesifsomethingbeginswithK,C,orZ,thenarl,theneithersora.ItdoesnomatchKCZarl.Negationispossibletoo,sotry
this:
if (/[KCZ]arl[^sa]/) {
whichreturnsthingsbeginningwithK,CorZ,thenarl,andthenanythingEXCEPTsora.Thecaret^ hastobethefirstcharacter,
otherwiseitdoesn'tworkasthenegation.Havingsaid[ ] definessinglecharactersonly,Ishouldmentionthanthesetwoarethe
same:
/[abcdeZ]arl/
/[a-eZ]arl/
ifyouuseahyphenthenyougetthelistofcharactersincludingthestartandfinishcharacters.Andifyouwanttomatchaspecial
character(metacharacter),youmustescapeit:
/[\-K]arl/
matchesKarlorarl.Althoughthe- characterisepeenedbytwocharacters,itisjusttheonecharactertomatch.
Matchingatspecificpoints
Ifyouwanttomatchattheendoftheline,makesurea$ isthelastcharacterintheregex.Thisonepullsoutallthosenames
endingina.Slotitintotheexampleabove:
if (/a$/) {

Andthereisacorrespondingcharacter,thecaret^ ,whichinthiscontextmatchesatthebeginningofthestring.Yes,thecaret
alsonegatesacharacterclasslikethis[^KCZ]arl butinthiscaseitanchorsthematchtothebeginningofthestring.
if (/n/i) {
if (/^n/i) {
The first one is true if the word contains an 'n' anywhere in it.
The second specifies that the 'n' must be at the beginning of the string to be
matched. Use this anchor where you can, because it makes the whole regex
faster, and safer if you know what the first character must be.

Negatingtheregex
Ifyouwanttonegatetheentireregexchange=~ to!~ (Remember! means'notequalto'.)
if ($_ !~/[KC]arl/) {
Of course, as we are testing $_
this works too:
if (!/[KC]arl/) {

ReturningtheMatch
Nowthingsgetinteresting.Whatifwewantpullsomethingoutofastring?Sofarallwehavedoneistestfortruth,thatissayyea
ornayifastringmatches,butnotreturnwhatwefound.Runthis:
$_='My email address is <[email protected]>.'
/(<robert\@netcat.co.uk>)/i
print "Found it ! $1\n"
4/10/12 Robert's Perl Tutorial
31/86 www.sthomas.net/roberts-perl-tutorial.htm
Firstly, note the single quotes when $_
is assigned. Ifthereweredoublequotes,we'dneed\@insteadof@.
Remember, double quotes "" allow variable interpolation, so Perl looks for an
array called @NetCat which does not exist.
Secondly,lookattheparensaroundtheentireregex.Ifyouuseparens,asideeffectisthatthefirstmatchisputintoavariable
called$1 .We'llgettothemaineffectlater.Thesecondmatchgoesinto$2 andsoon.Alsonotethatthe\@ hasbeenescaped,
soperldoesn'tthinkitisanarray.Remember\ eitherescapesaspecialcharacter,orgivesaspecialmeaning.Thinkofitas
Superman'stelephonebox.ImagineClarkKentwalkingaroundwithhismagicpartnerBackSlash.
Noticehowwespecifyintheregexcaseinsensitivitywith/i andtheregexreturnsthecasesensitivestringthatis,exactlywhat
itfound.
Trytheregexwithoutparens.Thentrythisone:
/<(robert)\@netcat.co.uk>/i
You can put the parens anywhere. More or less. Now, run this :
$_='My email address is <[email protected]>.'
/<(robert)\@(netcat.co.uk)>/i
print "Found it ! $1 at $2\n"
See, you can have more than one ! Look at the above regex. Looks
easy now, don't you think ? What about five minutes ago ? It would have looked
like a typing mistake ! Well, there are some hairier regex to come, but you'll
have a good barber.

*+regexesbecomelinenoise
Whatifwedidn'tknowwhattheemailaddresswasgoingtobe?
$_='My email address is <[email protected]>.'
print "Found it ! :$1:" if /(<.*>)/i
When you see an if
statement like this, read it right to left. The print statement is only executed if code on
the right of the expression is true.
We'lldiscussthis.Firstly,wehavetheopeningparens( .Soeverythingfrom( to) willbeputinto$1 ifthematchissuccessful.
Thenthefirstcharacterofwhatwearesearchingfor,< .Thenwehaveadot,orperiod. .Forthisregex,wecanassume.
matchesanycharacteratall.
Sowearenowmatching< followedbyanycharacter.The* means0ormoreofthepreviouscharacter.Theregexfinishesby
requiring> .
Thisisimportant.Getthebasicsrightandallregexareeasy(Ireadsomewhereonce).Anexamplebestillustratesthepoint.Slot
thisregexininstead:
$_='My email address is <[email protected]>.'
print "Found it ! :$1:" if /(<*>)/i
What's happening here ?
Theregexstarts,logically,atthestartofthestring.Thisdoesn'tmeanitstartsa'M',itstartsjustbeforeM.Thereisa'nothing'
betweenthestringstartand'M'.
Theregexissearchingfor<* ,whichis0ormore< .
Thefirstthingitfindsisnot< ,butthenothinginbetweenthestartofthestringandthe'M'from'Myemail...".Doesthismatch?
Astheregexislookingfor"0ormore"< ,wecancertainlysaythatthereare0< atthestartofthestring.Sothematchis,sofar,
successful.Wehavedealtwith<* .
4/10/12 Robert's Perl Tutorial
32/86 www.sthomas.net/roberts-perl-tutorial.htm
However,thenextitemtomatchis> .Unfortunately,thenextiteminthestringis'M',from'Myemail..".Thematchfailsatthis
point.Sure,itmatched< withoutanyproblem,butthecompletematchhastowork.
Theonlytwocharactersthatcanmatchsuccessfullyatthispointare< or> .The'point'beingthat<* hasbeenmatched
successfully,andweneedeither> tocompletethematchormoreof< tocontinuethe'0ormore'matchdenotedby* .
'M'isneitherofthem,soitfailsatthispoint,whenithasmatched
Quickclarificationtheregexcannotsuccessfullymatch< ,thenskiponaheadthroughthestringuntilitmatches> .The
charactersinthestringbetween<>alsoneedtomatchtheregex,andtheydon'tinthiscase.
Allisnotlost.Regexesarehardylittlebeastsanddon'tgiveupeasily.Anattemptismadetomatchtheregexwhereverpossible.
Theregexsystemkeepstryingthematchateverypossibleplaceinthestring,workingtowardstheend.
Let'slookatthematchwhenitreachesthe'm'in'work.com'.
Again,wehavehere0< .Sothematchworksasbefore.Aftersuccesson<* thenextcharacterisanalyseditisa> ,sothe
matchissuccessful.
But,bewarned.Thematchmaybesuccessfulbutyourjobisnotdone.Assumingtheobjectiveofwastoreturntheemailaddress
withintheanglebracketsthenthatregexisamiserablefailure.Watchfortrapsofthisnaturewhenregexing.
That's* explained.Justtoconsolidate,aquicklookat:
$_='My email address is <[email protected]>.'
print "Match 1 worked :$1:" if /(<*)/i
$_='<My email address is <[email protected]>.'
print "Match 2 worked :$1:" if /(<*)/i
$_='My email address is <[email protected]<<<<>.'
print "Match 3 worked :$1:" if /(<*>)/i
Match 1 is true. It doesn't return anything, but it is true
because there are 0 < at the very
start of the string.
Match2works.Afterthe0< atthestartofthestring,thereis1< sotheregexcanmatchthattoo.
Match3works.Afterthefailingonthefirst< ,itjumpstothesecond.Afterthat,thereareplentymoretomatchrightupuntilthe
requiredending.
Gladyoufollowedthat.Now,payevencloserattention!Concentratefullyonthetaskathand!Thisshouldbestraightforwardnow:
$_='HTML <I>munging</I> time !.'
/<I>(.*)<\/I>/i
print "Found it ! $1\n"
Pretty much the same as the above, except the parens are moved so
we return what's only inside the tags, not including the tags themselves. Also
note how / is escaped like so \/ otherwise Perl thinks that's the end of
the regex.
Now,supposewechange$_ to:
$_='HTML <I>munging</I> time is here <I>again</I> !.'
and run it again. Interesting effect, eh ? This is known as
Greedy Matching. What happens is that when Perl finds the initial match, that
is <I> it jumps right to the end
of the string and works back from there to find a match, so the longest string
matches. This is fine unless you want the shortest string. And there is a
solution:
/<I>(.*?)<\/I>/i
Just add a question mark and Perl does stingy matching. No
4/10/12 Robert's Perl Tutorial
33/86 www.sthomas.net/roberts-perl-tutorial.htm
nationalistic jokes. I have Dutch and Scottish friends I don't want to offend.
TheDifferenceBetween+and*
Youknowwhat* means,namelymatch0ormore.Ifyouwanttomatch1ormore,thenuse+ .Thedifferenceisimportant.
$_='The number is 2200 and the day is Monday'
($star)=/([0-9]*)/
($plus)=/([0-9]+)/
print "Star is '$star' and Plus is '$plus'\n"
You'll note that $star has no value. The match was
successful though. It managed to match 0 or more characters from 0 to 9 at the
very start of the regex.
Thesecondregexwith$plusworkedalittlebetter,becausewearematchingoneormorecharactersfrom0to9.Therefore,
unlessone0to9isfoundthematchwillfail.Oncea09isfound,thematchcontinuesaslongasthenextcharacteris09,thenit
stops.
Nowweknowthis,thereisanotherwaytoremoveanemailaddressfromwithinanglebrackets:
$_='My email address is <[email protected]> !.'
/<([^>]+)/i
print "Found it ! $1\n"
This regex matches <. Then the capturing parens
start. They have no effect on this regex other than to capture the match.
After that, there is a character class, containing one character. As ^
is the first character is the class, it negates the class. That's why
we are using a character class with only one character in it, because it can
be negated.
Sofarwehavematched<andanythingthatisnot>.The+ensureswematchasmanycharactersthatarenot<'saswecan.This
hasthesameeffectas.*?butismoreefficient.Itmayalsosuityourpurposes,as.*?reliesonyouknowingwhatyouwantto
matchupto,whereas[^>]+simplycontinesmatchinguntilitfindssomethingthatfailsitscriteria.Justmakesureyouunderstand
thedifferencebecauseitisacrucialpartofregexery.
Reusingthematch\1,$1...
Supposewedidn'tknowwhatHTMLtagwehadtomatch?ItcouldbeB,I,EMorwhatever,andwewanteverythingthatisin
between.Well,HTMLcontainertagslikeBandEMhaveendtagswhicharethesameasthestarttag,exceptforthe/.Sowhatwe
coulddois:
findoutwhatisinside<>
searchforexactlythesametag,butwiththeclosing/
returnwhateverisinbetween.
Canthisbedone?Ofcourse.Thisisperl,allthingsarepossible.Now,rememberthesideeffectofparens.IpromiseI'llexplainthe
primaryeffectatsomepoint.Ifwhateverisin(parens)matches,theresultisstoredinavariablecalled$1 .Sowecanuse
<(.*?)> whichwillfindus< thenasmanyanythings(the. and* )uptothenext,notlast> (the? forcesstingymatching).
Theresultisstoredin$1 becauseweusedparens.Next,weneedeverythinguptotheclosingtag.That'seasy:(.*?) matches
everythingupuntilthenextcharacterorsetofcharacters.Andhowexactlydowedefinewheretostop?
Wecanuse$1 eveninthesameregexitwasfoundin.However,itisnotreferredtowithinaregexas$1 ,but\1 .
Sowewanttomatch</$1> whichinperlcodeis<\/\1> .The/ mustbeescapedbecauseitistheendoftheregex,and1 is
escapedsoitrefersto$1 insteadofmatchingthenumber1.
4/10/12 Robert's Perl Tutorial
34/86 www.sthomas.net/roberts-perl-tutorial.htm
Stillhere?Thisiswhatitlookslike:
$_='HTML <I>munging</I> time is here <I>again</I> !.'
/<(.*?)>(.*?)<\/\1>/i
print "Found it ! $2\n"
If you want to know how to return all the matches above, read on.
But before that:

HowtoAvoidMakingMountainswhileEscapingSpecialCharacters
Youwanttomatchthishttp://language.perl.com/faq/ .That'sareal(useful)URLbytheway.Hint.Tomatchit,youneedto
dothis:
/http:\/\/language\.perl\.com\/faq\//
which should make the awful metaphor above clearer, if not
funnier. The slash, / , is not
normally a metacharacter but as it is being used for the regular expression
delimiters, it needs to be escaped. We already know that . is special.
Fortunatelyforoureyes,Perlallowsyoutopickyourdelimiterifyouprefixitwith'm'asthisexampleshows.We'llusea#:
m#http://language\.perl\.com/faq/#
Which is a huge improvement, as we change / to # .
We can go further with readability by quoting everything:
m#\Qhttp://language.perl.com/faq/\E#
The \Q escapes everything
up until \E or the regex delimiter (so
we don't really need the \E above). In this case #
will not be escaped, as it delimits the regex.
SomeoneoncepostedaquestionaboutthistothePerlWin32UsersmailinglistandIwassointriguedaboutthisapparently
undocumentedtrickIspentthenexttwentyminutesfiguringitoutbytrialanderror,andpostedareply.NextdayIfoundlotsof
messagestellingthepostertoreadthemanualbecauseitwasclearlydocumented.<facecolour='red'intensity='high'>Myexcuse
wasIdidn'thavethedocstohand....moralofthestoryRTFMandRTFFAQs!
SubsitutionandYetMoreRegexPower
Basicchanges
Supposeyouwanttoreplacebitsofastring.Forexample,'us'with'them'.
$_='Us ? The bus usually waits for us, unless the driver forgets us.'
print "$_\n"
s/Us/them/ # operates on $_, otherwise you need $foo=~s/Us/them/
print "$_\n"
What happens here is that the string 'Us' is searched for, and
when a match is found it is replaced with the right side of the expression, in
this case 'them'. Simple.
You'llnoticethatonlyonesubstitutionwasmade.Tomatchgloballyuse/g whichrunsthroughtheentirestring,changing
whereveritcan.Try:
s/Us/them/g
4/10/12 Robert's Perl Tutorial
35/86 www.sthomas.net/roberts-perl-tutorial.htm
which fails. This is because regexes are not, by default,
case-sensitive. So:
s/us/them/ig
would be a better bet. Now, everything is changed. A little too
much, but one problem at a time. Everything you have learn about regex so far
can be used with s/// , like parens,
character classes [ ] , greedy and
stingy matching and much more. Deleting things is easy too. Just specify
nothing as the replacement character, like so s/Us// .
Sowecanusesomeofthatknowledgetofixthisproblem.Weneedtomakesurethataspaceprecedesthe'us'.Whatabout:
s/ us/them/g
An small improvement. The first 'Us' is now no longer changed,
but one problem at a time ! We'll first consider the problem of the regex
changing 'usually' and other words with 'us' in them.
Whatwearelookingforisaspace,then'us',thenacomma,periodorspace.Weknowhowtospecifyoneofanumberofoptions
thecharacterclass.
s/ us[. ,]/them/g
Another tiny step. Unfortunately, that step wasn't really in the
right direction, more on the slippery slope to Poor Programming Practice. Why?
Because we are limiting ourselves. Suppose someone wrote ' send it to us
when we get it'.
Youcan'tthinkofallthepossiblepermutations.Itisofteneasier,andsafer,tosimplystatewhatmustnofollowthematch.Inthis
case,itcanbeanythingexceptaletter.Wecandefinethatasaz.Sowecanaddthattotheregex.
s/ us[^a-z]/ them/g
the caret ^ negates the
character class, and a-z represents
every alphabet from a to z inclusive. A space has been added to the
substitution part - as the original space was matched, it should be replaced
to maintain readability.
\w
Whatwouldbemoreusefulistousea-zA-Z instead.Ifweweren'tusing/i we'dneedthat.Asa-zA-Z issuchacommon
construct,Perlprovidesaneasyshorthand:
s/ us[^\w]/ them/g
The\w constructactuallymeans'word'equivalenttoa-zA-Z_0-9 .Sowe'llusethatinstead.
Tonegateanyconstruct,simplycapitaliseit:
s/ us[\W]/ them/g
andofcoursewedon'tneedthenegatingcaretnow.Infact,wedon'tevenneedthecharacterclass!
s/ us\W/ them/g
Sofar,sogood.Matchingthefirst'us'isgoingtobedifficultthough.Fortunately,thereisaneasysolution.We'veseenPerl's
definitionofaword\w .Betweeneachwordisaboundary.Youcanmatchthiswith\b .
s/\bus\W/ them/g
that's\b followedby'us',not'bus':)
Now,werequireawordboundarybefore'us'.Asthereisa'nothing'atthestartofthestring,wehaveamatch.Thereisaspace
4/10/12 Robert's Perl Tutorial
36/86 www.sthomas.net/roberts-perl-tutorial.htm
afterthefirst'Us',sothematchissuccessful.Youmightnoticeanextraspacehascreptinthat'sthespaceweaddedearlier.The
matchdoesn'tincludethespaceanymoreitmatchesonthewordboundary,thatisjustbeforethewordbegins.Thespace
doesn'tcount.
Didyounoticethefinalperiodandthecommaarereplaced?Theyarepartofthematchitisthe\W thatmatchesthem.Wecan't
avoidthat.Wecanhoweverputbackthatpartofthematch.
Replacingwithwhatwasfound
s/\bus(\W)/them\1/g
Westartwithcapturingwhateverthe\W matches,usingparens.Then,weaddittothereplacementstring.Thecaptureisof
coursein$1 ,butasitisinaregexwerefertoitas\1 .
Thefinalproblemisofcoursecapitalisingthereplacementstringwhenappropriate.WhichinoldversionsofthetutorialIleftasan
exercisetothereader,havingrunoutofmotivation.AreaderbythenameofPaulTrafforddulysolvedtheproblem,andIhavejust
insertedhisexcellentexplanationfortheelucidationofallconcerned:
# Solution to the us/them problem...
#
# The program works through the text assigning the
# variable $1 to 'U' or 'u' for any words where this
# letter is followed by 's' and then by non 'word'
# characters. The latter is assigned to variable $2.
#
# For each such matching occurrence, $1 is replaced by
# the letter that precedes it in the alphabet using
# operations 'ord' and 'chr' that return the ASCII value
# of a character and the character corresponding to a
# given natural number. After this 'hem' is tacked on
# followed by $2, to retain the shape of the original
# sentence. The '/e' switch is used for evaluation.
#
# NOTES
# 1. This solution will not replace US (short for
# United States) with Them or them.
#
# 2. If a 'magical' decrement operator '--' existed for
# strings then the solution could be simplified for we
# wouldn't need to use the 'chr' and 'ord' operators.
$_='Us ? The bus usually waits for us, unless the driver forgets us.'
print "$_\n"
s/\b([Uu])s(\W)/chr(ord($1)-1).hem.$2/eg
print "$_\n"

Anexcellentsolution,thanksPaul.
Thereareseveralmoreconstructs.We'lltakeaquicklookat\d whichmeansanythingthatisadigit,thatis0-9 .Firstwe'lluse
thenegatedform,\D ,whichisanythingecep0-9 :
print "Enter a number :"
chop ($input=<STDIN>)
if ($input=~/\D/) {
print "Not a number !!!!\n"
} else {
print 'Your answer is ',$input x 3,"\n"
}
thischecksthattherearenononnumbercharactersin$x .It'snotperfectbecauseit'llchokeondecimalpoints,butit'sjustan
example.Writingyourownnumbercheckerisactuallyquitedifficult,butitisaninterestingexercise.Tryit,andseehowaccurate
yoursis.
4/10/12 Robert's Perl Tutorial
37/86 www.sthomas.net/roberts-perl-tutorial.htm
x
Ihopeyoutrustedmeandtypedtheaboveinexactlyasitisshow(orpastedit),becausethex isnotamistake,itisafeature.If
youweretoosmartandchangedittoa* orsomethingchangeitbackandseewhatitdoes.
Ofcourse,thereisanotherwaytodoit:
unless ($input=~/\d/) {
print 'Your answer is ',$input x 3,"\n"
} else {
print "Not a number !!!!\n"
}
whichreversesthelogicwithanunless statement.
MoreMatching
Assumewehave:
$_='HTML <I>munging</I> time is here <I>again</I> !.'
andwewanttofindalltheitalicwords.Weknowthat/g willmatchglobally,sosurelythiswillwork:
$_='HTML <I>munging</I> time is here <I>again</I> ! What <EM>fun</EM> !'
$match=/<i>(.*?)<\/i>/ig
print "$match\n"
exceptitreturns1,andthereweredefinitelytwomatches.Thematchoperatorreturnstrueorfalse,notthenumberofmatches.So
youcantestitfortruthwithfunctionslikeif, while, unless Incidentally,thes/// operatordoesreturnthenumberof
substitutions.
Toreturnwhatismatched,youneedtosupplyalist.
($match) = /<i>(.*?)<\/i>/i
whichhandilyputsallthefirstmatchinto$match .Notethatan= isused(forassignment),asopposedto=~ (topointtheregexat
avariableotherthan$_.
Theparensforcealistcontextinthiscase.Thereisjusttheoneelementinthelist,butitisstillalist.Theentirematchwillbe
assignedtothelist,orwhateverisintheparens.Tryaddingsomeparens:
$_='HTML <I>munging</I> time is here <I>again</I> ! What <EM>fun</EM> !'
($word1, $word2) = /<i>(.*?)<\/i>/ig
print "Word 1 is $word1 and Word 2 is $word2\n"
Intheexampleabovenotice/g hasbeenaddedsoaglobalreplacementisdonethismeansperlcarriesonmatchingevenafter
itfindsthefirstmatch.Ofcourse,youmightnotknowhowmanymatchestherewillbe,soyoucanjustuseanarray,oranyother
typeoflist:
$_='HTML <I>munging</I> time is here <I>again</I> ! What <EM>fun</EM> !'
@words = /<i>(.*?)<\/i>/ig
foreach $word (@words) {
print "Found $word\n"
}
and@words willbegrowntotheappropriatesizeforthematches.Youreallycansupplywhatyouliketobeassignedto:
($word1, @words[2..3], $last) = /<i>(.*?)<\/i>/ig
you'llneedmoreitalicsforthatlastonetowork.Itwasonlyademonstration.
4/10/12 Robert's Perl Tutorial
38/86 www.sthomas.net/roberts-perl-tutorial.htm
Thereisanothertrickworthknowing.Becausearegexreturnstrueeachtimeitmatches,wecantestthatanddosomethingevery
timeitreturnstrue.Theidealfunctioniswhile whichmeans'dosomethingaslongtheconditionI'mtestingistrue'.Inthiscase,
we'llprintoutthematcheverytimeitistrue.
$_='HTML <I>munging</I> time is here <I>again</I> ! What <EM>fun</EM> !'
while (/<(.*?)>(.*?)<\/\1>/g) {
print "Found the HTML tag $1 which has $2 inside\n"
}
Sothewhileoperatorrunstheregex,andifitistrue,carriesoutthestatementsinsidetheblock.
Tryrunningtheprogramabovewithoutthe/g .Noticehowitloopsforever?That'sbecausetheexpressionalwaysevaluatesto
true.Byusingthe/g weforcethematchtomoveonuntiliteventuallyfails.
Nowweknowthis,aneasywaytofindthenumberofmatchesis:
$_='HTML <I>munging</I> time is here <I>again</I> ! What <EM>fun</EM> !'
$found++ while /<i>.*?<\/i>/ig
print "Found $found matches\n"
Youdon'tneedbracesinthiscaseasnothingapartfromtheexpressiontobeevaluatedfollowsthewhile function.
ParenthesesAgain:OR
Therealuseforthem.Precedence.Trythis,andyesyoucantryitathome:
$_='One word sentences ? Eliminate. Avoid clichs like the plague. They are old hat.'
while (/o(rd|ne|ld)/gi) {
print "Matched $1\n"
}
Firstly,noticethesubtleintroductionoftheor operator,inthiscase| ,thepipe.WhatIreallywanttoexplainhowever,isthatthis
regexmatchesofollowedbyrd,neorld.Withouttheparensitwouldbe/ord|ne|ld/ whichisdefinitelynotwhatwewant.That
matchesjustplainord,orneorld.
(?:OREfficiency)
Intheinterestsofefficiency,considerthis:
print "Give me a name :"
chop($_=<STDIN>)
print "Good name\n" if /Pe(tra|ter|nny)/
Thecodeabovefunctionscorrectly.Ifyouwerewonderingwhatagoodnameis,Petra,PeterandPennyqualify.Theregexisnot
asefficientasitcouldbethough.ThinkaboutwhatPerlisdoingwiththeregex,thatyouarejustignoring.Simplythrowingaway
casually.Withoutconsiderationastotheeffortthathasgoneintocreatingitforyou.Theresourcessquandered.Thelittlebytesof
memorywhosesolefunctioninlifeistostorethisinformation,whichwillneverbeused.
What'shappeningisthatbecauseparensareused,perliscreating$1foryourusageandabusage.Whilethismaynotseem
important,afairamountofresourcesgointocreating$1,$2andsoon.Notsomuchthememoryusedtostorethem,morethe
CPUeffortinvolved.So,ifyouaren'tgoingtousetheparensforcapturingpurposes,whybothercapturingthematch?
print "Give me a name :"
chop($_=<STDIN>)
print "Good name\n" if /Pe(?:tra|ter|nny)/
print "The match is :$1:\n"
Thesecondprintstatementdemonstratesthatnothingiscapturedthistime.Yougetthebenefitsoftheparen'sprecedence
changingcapabilities,butwithouttheoverheadofthecapturing.ThisbenefitisespeciallyworthwhileifyouarewritingCGI
programswhichuseparensinregexwithCGI,everylittleofbitefficiencycounts.
4/10/12 Robert's Perl Tutorial
39/86 www.sthomas.net/roberts-perl-tutorial.htm
Matchingspecificamountsof...
Finally,takealookatthis:
$_='I am sleepy....zzzz....DING ! Wake Up!'
if (/(z{5})/) {
print "Matched $1\n"
} else {
print "Match failed\n"
}
Thebraces{ } specifyhowmanyoftheprecedingcharactertomatch.Soz{2} matchesexactlytwo'z'sandsoon.Change
z{5} toz{4} andseehowitworks.Andthere'smore...
/z{3}/ 3zonly
/z{3,}/ Atleast3z
/z{1,3}/ 1to3z
/z{4,8}/ 4to8z
Toanyoftheaboveyoumaysuffixanquestionmark,theeffectofwhichisdemonstratedinthefollowingprogram.Runitacouple
oftimes,inputting2,3and4:
print "How many letters do you want to match ? "
chomp($num=<STDIN>)
# we assign and print in one smooth move
print $_="The lowest form of wit is indeed sarcasm, I don't think.\n"
print "Matched \\w{$num,} : $1 \n" if /(\w{$num,})/
print "Matched \\w{$num,?}: $1 \n" if /(\w{$num,}?)/

Thefirstmatchis'matchanyword(that'sa-Z0-9_)equaltoorlongerthan$num character,andreturnit.'Soifyouenter4,then
'lowest'isreturned.Theword'The'doesn'tmatch.
Thesecondmatchisexactlythesame,butthe? forcesaminimalmatch,soonlythepartactuallymatchedisreturned.
Justtoclearthisup,amendtheprogramthus:
print "\nMatched \\w{$num,} :"
print "$1 " while /(\w{$num,})/g
print "\nMatched \\w{$num,?} :"
print "$1 " while /(\w{$num,}?)/g

Notetheadditionof/g .Tryitwithoutnoticehowthematchnevermoveson?
Pre,Post,andMatch
AndnowontheRegexProgrammeToday,wehavegueststarsPrematch,PostmatchandMatch.Allofwhomaregoingtoslowour
entireprogrammedown,butareusefulanyway:
$_='I am sleepy....snore....DING ! Wake Up!'
/snore/ # look, no parens !
print "Postmatch: $'\n"
print "Prematch: $`\n"
print "Match: $&\n"
Ifyouarewonderingwhatthedifferencebetweenmatchandusingparensisyoushouldrememberthanyoucanmovetheparens
4/10/12 Robert's Perl Tutorial
40/86 www.sthomas.net/roberts-perl-tutorial.htm
around,butyoucan'tvarywhat$& anditsilkreturn.Also,usinganyoftheabovethreeoperatorsdoesslowyourentireprogram,
whereasusingparenswilljustslowtheparticularregexyouusethemfor.However,onceyou'veusedoneofthethreematchesyou
mightaswellusethemallovertheplaceasyou'vepaidthespeedpenalty.Useparenswherepossible.
RHSExpressions
/e
RHSmeansRightHandSide.SupposewehaveanHTMLfile,whichcontains:
<FONT SIZE=2> <FONT SIZE=4> <FONT SIZE=6>
andwewishtodoublethesizeofeachfontso2becomes4and4becomes8etc.Whatabout:
$data="<FONT SIZE=2> <FONT SIZE=4> <FONT SIZE=6>"
print "$data\n"
$data=~s/(size=)(\d)/\1\2 * 2/ig
print "$data\n"
whichdoesn'treallyworkout.Whatthisdoesismatchsize=x,wherexisanydigit.Thefirstmatch,size=,goesinto$1 andthe
secondmatch,whateverthedigitis,goesinto$2 .Thesecondpartoftheregexsimplyprints$1 and$2 (referredtoas\1 and
\2 ),andattemptstomultiply$2 by2.Remember/i meanscaseinsensitivematching.
Whatweneedtodoisevaluatetherighthandsideoftheregexasanexpressionthatisnotjustprintoutwhatitsays,butactually
evaluateit.Thatmeansworkitthrough,notblindlytreatitasstring.Perlcandothis:
$data=~s/(size=)(\d)/$1.($2 * 2)/eig
Alittleexplanation....theLHSisthesameasbefore.Weadd/e soPerlevaluatestheRHSasanexpression.Soweneedto
change\1 into$1 andsoon.Theparensaretheretoensurethat$2 * 2 isevaluated,thenjoinedto$1.Andthat'sit!
/ee
Itisevenpossibletohavemorethanone/e .Forexample:
$data='The function is <5funcA>'
$funcA='*2+4'
print "$data\n"
$data=~s/<(\d)(\w+)>/($1+2).${$2}/ # first time
# $data=~s/<(\d)(\w+)>/($1+2).${$2}/e # second time
# $data=~s/<(\d)(\w+)>/($1+2).${$2}/ee # third time
print "$data\n"
Toproperlyappreciatethisyouneedtorunitthreetimes,eachtimecommentingoutadifferentline.Onlyoneregexlineshouldbe
uncommentedwhentheprogramisrun.
Thefirsttimeroundtheregexisadumbvariableinterpolation.Perljustsearchesthestringforanyvariables,finds$1and$2,and
replacesthem.
Secondtimeroundtheexpressionisevaluated,asopposedtojustplainvariableinterpolated.Thismeansthat$1+2isevaluated.
$1hasavalueof5,pl,plus2==7.Theotherpartofthereplacement,${$2}isevaluatedonlysofarasworkingoutthatthe
variablenamed$2shouldbeplacedinthestring.
ThirdtimeroundandPerlnowmakesasecondpassthroughthestring,lookingforthingstodo.Afterthefirstpass,andjustbefore
thatsecondpassthestringlookslikethis7*2+4 .Perlevaluatesthis,andprintstheresult.
Sothemore/e 'syouaddontheendoftheregex,themorepassesPerlmakesthroughthereplacementstringtryingtoevaluate
thecode.
Thisisfairlyadvancedstuffhere,anditisprobablynotsomethingyouwilluseeveryday.Butknowingitisthereishandy.
4/10/12 Robert's Perl Tutorial
41/86 www.sthomas.net/roberts-perl-tutorial.htm
AWorkedExample:DateChange
ImagineyouhavealistofdateswhichareintheUSformatofmonth,day,yearasopposedtotherestoftheworld'slogicalnotion
ofday,monthyear.Weneedaregextotransposethedayandmonth.Thedatesare:
@dates=(
'01/22/95',
'05/15/87',
'8-13-96',
'5.27.78',
'6/16/1993'
)
Thetaskcanbesplitintostepssuchas:
1. Matchthefirstdigit,ortwodigits.Capturethisresult.
2. Matchthedelimiter,whichappearstobeoneof/ - .
3. Matchthesecondtwodigits,andcapturethatresult
4. Rebuildthestring,butthistimereversingthedayandmonth.
Thatmaynotbeallthesteps,butitiscertainlyenoughforastart.Planningregexisimportant.So,firstpass:
@dates=(
'01/22/95',
'5/15/87',
'8-13-96',
'5.27.78',
'6/16/1993'
)
foreach (@dates) {
print
s#(\d\d)/(\d\d)#$2/$1#
print " $_\n"
}
Hmm.Thishasn'tworkedforthedatesdelimitedwith- . ,andthelastdatehasn'tworkedeither.Thefirstproblemisprettyeasy
wearejustmatching/ ,nothingelse.Thesecondproblemarisesbecausewearematchingtwodigits.Therefore,5/15/87is
matchedonthe15and87,notthe5and15.Thedate6/16/1993ismatchedonthe16andthe19of1993.
Wecanfixbothofthose.First,we'llmatcheither1or2digits.Thereareafewwaysofdoingthis,suchas\d{1,2}whichmeans
either1ortwooftheprecedingcharacter,orperhapsmoreeasily\d\d?whichmeansmatchone\dandtheotherdigitisoptional,
hencethequestionmark.Ifweused\d+thenthatwouldmatch19988883whichisnotavaliddate,atleastnotasfarasweare
concerned.
Secondly,we'lluseacharacterclassforallthepossibledatedelimiters.Hereisjusttheloopwiththoseamendments:
foreach (@dates) {
print
s#(\d\d?)[/-.](\d\d?)#$2/$1#
print " $_\n"
}
whichfails.Examinetheerrorstatementcarefully.Thekeywordis'range'.Whatrange?Well,therangebetween/and.because-
istherangeoperatorwithinacharacterclass.Thatmeansitisaspecialcharacter,orametacharacter.Andtonegatethespecial
meaningofmetacharacterswehavetouseabackslash.
Butwait!Idon'thearyoucry.Surely. isametacharactertoo?Itis,butnotwithinacharacterclasssoitdoesn'tneedtobe
escaped.
foreach (@dates) {
print
s#(\d\d?)[/\-.](\d\d?)#$2/$1#
print " $_\n"
}
Nearlythere.However,wearealwaysreplacingthedelimiterwith/whichismessy.That'saneasyfix:
foreach (@dates) {
4/10/12 Robert's Perl Tutorial
42/86 www.sthomas.net/roberts-perl-tutorial.htm
print
s#(\d\d?)([/\-.])(\d\d?)#$3$2$1#
print " $_\n"
}
sothatfixesthat.Incaseyouwerewondering,the. dotdoesnotactas'1ofanything'insideacharacterclass.Itwoulddefeatthe
objectofthecharacterclassifitdid.Soitdoesn'tneedescaping.Thereisafurtherimprovementyoucanmaketothisregex:
$m='/.-'
foreach (@dates) {
print
s#(\d\d?)([$m])(\d\d?)#$3$2$1#
print " $_\n"
}
whichisgoodpracticebecauseyouareboundtowanttochangeyourdelimitersatsomepoint,andputtingtheminsidetheregexis
hardcording,andweallknowthatendsintears.Youcanalsoreusethe$mvariableelsewhere,whichisgoodpratice.
Didyounoticethedifferencebetweenwhatweassignto$mandwhatwehadbefore?
/\-.
$m='/.-'
Thedifferenceisthatthe- isnolongerescaped.Whynot?Logic.Perlknows- istherangeoperator.Therefore,theremustbea
charactertotheimmediateleftandimmediaterightofitinorderforittowork,forexamplee-f.Whenweassignastringto$m,the
rangeoperatoristhelastcharacterandthereforehasnocharactertotherightofit,soPerldoesn'tinterpretasarangeoperator.
Trythis:
$m='/-.'
andwatchitfail.
Somethingelsethatcausesheartacheismatchingwhatyoudon'tmeanto.Trythis:
@dates=(
'01/22/95',
'5/15/87',
'8-13-96',
'5.27.78',
'/16/1993',
'8/1/993',
)
$m='/.-'
foreach (@dates) {
print
s#(\d\d?)([$m])(\d\d?)#$3$2$1# or print "Invalid date! "
print " $_\n"
}
Thetwoinvaliddatesattheendareletthrough.Ifyouwantedtocheckthevalidityofeverypossibledatesincethestartofthe
moderncalendarthenyoumightbebetteroffwithadatabaseratherthanaregex,butwecandosomebasicchecking.The
importantpointisthatweknowthelimitationsofwhatwearedoing.
Whatwecandoismakesureoftwothingsthattherearethreesetsofdigitsseperatedbyourchosendelimiters,andthatthelast
setofdigitsiseithertwodigits,eg99,98,87,orfourdigits,eg1999,1998,1987.
Howcanwedothis?Extendthematch.Aftertheseconddigitmatchweneedtomatchthedelimteragain,theneither2digitsor
fourdigits.Howabout:
$m='/.-'
foreach (@dates) {
print
s#(\d\d?)([$m])(\d\d?)[$m](\d\d|\d{4})#$3$2$1$2# or print "Invalid date! "
print " $_\n"
}
4/10/12 Robert's Perl Tutorial
43/86 www.sthomas.net/roberts-perl-tutorial.htm
whichdoesn'treallyworkout.Theproblemisitlets993through.Thisisbecause\d\dwillmatchonthefrontof993.Furthermore,
wearen'tfixingtheyearbackontotheendresult.
Thedelimitermatchisalsofaulty.Wecouldmatch/asthefirstdelimiter,andasthesecond.So,threeproblemstofix:
foreach (@dates) {
print
s#(\d\d?)([$m])(\d\d?)\2(\d\d|\d{4})$#$3$2$1$2$4# or print "Invalid!"
print " $_\n"
}
Thisisnowlookinglikeaseriousregex.Changes:
1. Wearereusingthesecondmatch,whichisthedelimiter,furtheronintheregex.That'swhatthe\2 is.Thisensuresthe
seconddelimiteristhesameasthefirstone,so5/798getsrejected.
2. The$ontheendmeansendofstring.Nothingallowedafterthat.Sotheregexnowhastofindeither2or4digitsattheend
ofthestring,oritfails.
3. Addedthematchoftheyear($4)totherebuildsectionoftheregex.
Regexcanbeascomplexasyouneed.Thecodeabovecanbeimprovedstillfurther.Wecouldrejectallyearsthatdon'tbeginwith
either19or20iftheyarefourdigityears.Theotherproblemwiththecodesofaristhatitwouldrejecta date like 02/24/99
which is validbecausetherearecharactersaftertheyear.Bothcanbefixed:
@dates=(
'01/22/95',
'5/15/87',
'8-13-96',
'5.27.78',
'/16/1993',
'8/1/993',
'3/29/1854',
'! 4/23/1972 !',
)
$m='/.-'
foreach (@dates) {
print
s#(\d\d?)([$m])(\d\d?)\2(\d\d|(?:19|20)\d{2})(?:$|\D)#$3$2$1$2$4# or print "Invalid!"
print " $_\n"
}
WehavenowgotanestedOR,andtheinnerORisnoncapturingforreasonsofefficiencyandreadability.Attheendwealternate
betweenlettingtheregexmatcheitheranendoflineoranynondigit,symbolisedwith\D.
Wecouldgoon.Itisoftenverydifficulttowritearegexthatmatchesanythingofevenminorcomplexitywithabsolutecertainity.
ThinkaboutIPaddressesforexample.Whatisimportantistobuildtheregexcarefully,andunderstandwhatitcanandcannotdo.
Catchinganythingsupposedlyinvalidisagoodideatoo.Testyourregexwithallsortsofinvaliddata,andyou'llunderstandwhatit
cando.
SplitandJoin
Splitting
Whileyouareintheregexmood,aquicklookatsplit andjoin .Destructionisalwayseasier(justaskyourcarmechanic),so
letsstartwithsplit .
$_='Piper:PA-28:Archer:OO-ROB:Antwerp'
@details=split /:/, $_
foreach (@details) {
print "$_\n"
}
4/10/12 Robert's Perl Tutorial
44/86 www.sthomas.net/roberts-perl-tutorial.htm
Herewegivesplit isgiventwoarguments.Thefirstoneisaregexspecifyingwhattospliton.Thenextiswhattosplit.Actually,I
couldleave$_ outbecauseasusualitisthedefaultifnothingisspecified.
Theassignmentcaneitherbeascalarvariableoralistlikeanarray(orhash,butatthistime'hash'toyoumeanswhatyouthink
theDutchdoorasillydrinkingeventspoiltbysomerunning).Ifit'sascalarvariableyougetthenumberofelementsthesplithas
splut.Shouldthatbe'thesplithassplittered'or'thesplithassplat'.Hmmm.Probably'thesplithassplit'.YouknowwhatImean.I
thinkIjustgeneratedaFatalErrorinEnglish.dll.Whoops.Inanycase,splittingtoascalarvariableisnotalwaysaGoodThing,as
we'llseelater.
Iftheassignmentisanarray,thenasyoucanseeintheaboveexamplethearrayiscreatedwiththerelevantelementsinorder.
Youcanalsoassigntoscalars,forexample:
$_='Piper:PA-28:Archer:OO-ROB:Antwerp'
($maker,$model,$name,$reg,$location) = split /:/, $_
(@aircraft[0..1],$aname,@regdetails) = split /:/, $_
$number=split /:/ # not bothering with the $_ at the end, as it is the default
print "Using the first 'split'\n"
print "$reg is a $maker $model $name based in $location\n"
print "There are $number details available on this aircraft\n\n"
print "Using the second 'split'\n"
print "You can find $regdetails[0], an $aircraft[1], $regdetails[1]\n"
Thisdemonstratesthatalistcanbealistofscalarvariables(whichisbasicallywhatanarrayisanyway),andthatyoucaneasily
seehowmanyelementstheexpressioncanbesplitinto.
Theexamplebelowaddsathirdparametertosplit,whichishowmanyelementsyouwantreturned.Ifyoudon'twanttheextrastuff
attheendpop it.
$_='Piper:PA-28:Archer:OO-ROB:Antwerp'
@details=split /:/, $_, 3
foreach (@details) {
print "$_\n"
}
Intheexamplebelowwesplit onwhitespace.Whitespace,inperlterms,isaspace,tab,newline,formfeedorcarriagereturn.
Insteadofwriting\t\n\f\rforeachoftheabove,youcansimplyuse\s ,orthenegatedversion\S whichmeansanything
exceptwhitespace.Thinkofwhitespaceasanythingyouknowisthere,butyoucan'tsee.
Thewhitespacesplit isspeciallyoptimisedforspeed.I'veusedspaces,doublespaces,atabandanewlineinthelistbelow.Also
notethe+ ,whichmeansoneormoreoftheprecedingcharacter,soitwillsplit onanycombinationofwhitespace.AndIthink
thefinalsplit isusefultoknow.Thesplit functiondoesnotreturnthedelimiter,sointhiscasethewhitespacewillnotbe
returned.
$_='Piper PA-28 Archer OO-ROB
Antwerp'
@details=split /\s+/, $_
foreach (@details) {
print "$_\n"
}
@chars=split //, $details[0]
foreach $char (@chars) {
print "$char !\n"
}

AveryFAQ
4/10/12 Robert's Perl Tutorial
45/86 www.sthomas.net/roberts-perl-tutorial.htm
ThefollowingquestionhascomeupatleastthreetimesinthePerlWin32Usersmailinglist.Canyouanswerit?
"My data is delimited by |, for example:
name|age|sex|height|
Why doesn't
@array=split /|/, $line
work ?"
Whyindeed.Ifyoudon'talreadyknowtheanswer,somesimpletroubleshootingstepscanbeapplied.First,createasample
programandrunit.
$line='name|age|sex|height'
@array=split /|/,$line
foreach (@array) { print "$_\n" }
Theeffectistosplit eachcharacter.The| isreturned.Asitisthedelimiter,| shouldbeignored,notreturned.
Atthispointyoushouldbethinking'metacharacter'.Alittleresearch(lookingatthedocumentation)willrevealthat| isindeeda
metacharacter,whichmeans'or',wheninsidearegex.So,ineffect,theregex/|/ means'nothing,ornothing'.Thesplit is
thereforeperformedon'nothings',andthereare'nothings'inbetweeneachcharacter.Thesolutioniseasy/\|/ .
$line='name|age|sex|height'
@array=split /\|/,$line
foreach (@array) { print "$_\n" }

Sothat'sthefunstuff,destruction.Nowtoputitbacktogetheragainwithjoin .
WhatHumptyDumptyneeds:Join
$w1="Mission critical ?"
$w2="Internet ready modems !"
$w3="J(insert your cool phrase here)" # anything prefixed by 'J' is now cool -)
$w4="y2k compatible."
$w5="We know the Web."
$w6="...the leading product in an emerging market."
$cool=join ' ', $w1,$w2,$w3,$w4,$w5,$w6
print $cool
Jointakesa'glue'operator,whichisnoaregularexpression.Itcanbeascalarvariablehowever.Inthiscaseitisaspace.Thenit
takesalist,whichcaneitherbealistofscalarvariables,anarrayorwhateveraslongasitsalist.Andyoucanseewhattheresult
is.Youcouldassignittoanarray,butyou'dendupwitheverythinginthefirstelementofthearray.
Theexamplebelowaddsanarrayintothelist,anddemonstratesuseofavariableasthedelimiter.
$w1="Mission critical ?"
$w2="Internet ready modems !"
$w3="J(insert your cool phrase here)" # anything prefixed by 'J' is now cool -)
$w4="y2k approved, tested and safe !"
$w5="We know the Web."
$w6="...the leading product in an emerging market."
@morecool=("networkable","compatible")
$sep=" "
$cool=join $sep, $w1,$w2,$w3,@morecool,$w4,$w5,$w6
print $cool

4/10/12 Robert's Perl Tutorial
46/86 www.sthomas.net/roberts-perl-tutorial.htm
Arecap,butwithsomenewfunctions
Randomness
Aren'tyouwishingyoucouldmixandmatchrandomlysoyoutoocouldgetajobmarketingvapourware?Heh.
@cool=(
"networkable directory services",
"legacy systems compatible",
"Mission critical, Business Ready",
"Internet ready modems !",
"J(insert your cool phrase here)",
"y2k approved, tested and safe !",
"We know the Web. Yeah.",
"...the leading product in an emerging market."
)
srand
print "How many phrases would you like (max ",scalar(@cool),") ?"
while (1) {
chop ($input=<STDIN>)
if ($input <= scalar(@cool) and $input > 0) {
last
}
print 'Sorry, invalid input, try again :'
}
for (1..$input) {
$index=int(rand $#cool)
print "$cool[$index] "
splice @cool, $index, 1
}
Afewthingstoexplain.Firstly,while (1) { .Wewantaneverlastingloop,andthisonewaytodoit.1isalwaystrue,soroundit
goes.Wecouldtest$input directly,butthatwouldn'tallowlast tobedemonstrated.
Everlastingloopsaren'tusefulunlessyouareapoliticianbeinginterviewed.Weneedtobreakoutatsomepoint.Thisisdoneby
thelast function.When$input isbetween1andthenumberofelementsin@cool thenoutwego.(Youcanalsobreakoutto
labels,incaseyouwerewondering.Andbreakoutinasweat.Don'tstartnowifyouweren't.)
Thesrand operatorinitialisestherandomnumbergenerator.Worksokforus,butCGIprogrammersshouldthinkofsomething
differentbecausetheirprogramsaresofrequentlyrun(theyhope:).
rand generatesarandomnumberbetween0and1,or0andanumberitisgiven.Inthiscase,thenumberofelementsof@cool
1,sofrom0to7.Thereisnopointgeneratingnumbersbetween1and8becausethearrayelementsrunfrom0to7.
Theint functionmakessureitisaninteger,thatisnomessybitsafterthedecimalpoint.
Thesplice functionremovestheprintedelementfromthearraysoitwon'tappearagain.Don'twanttostressthepoint.
Concatenation
Thereisanotherjoiningoperator,thistimethehumbledot,orperiod:. .Thisconcatanates(joins)variables:
$x="Hello"
$y=" World"
$z="\n"
print "$x\n" # print $x and a newline
$prt=$x.$y.$z # make a new var $prt out of $x, $y and $z
print $prt
4/10/12 Robert's Perl Tutorial
47/86 www.sthomas.net/roberts-perl-tutorial.htm
$x.=$y." again ".$z # add stuff to $x
print $x

Files
Opening
Perlisverygoodathandlingfiles.Create,inyourperlscriptsdirectoryc:\scripts,afilecalledstuff.txt.Copythefollowing
intoit:
The Main Perl Newsgroup:comp.lang.perl.misc
The Perl FAQ:http://www.perl.com/faq/
Where to download perl:http://www.activestate.com/
Now,toopenanddothingswiththisfile.First,wemustopenthefileandassignittoafilehandle.Alloperationswillbedoneonthe
fileviathefilehandle.Earlier,weused<STDIN> asafilehandlewereadfromit.
$stuff="c:\scripts\stuff.txt"
open STUFF, $stuff
while (<STUFF>) {
print "Line number $. is : $_"
}
Whatthisscriptdoesisfail.Whatisholddoisopenthefiledefinedin$stuff ,assignittothefilehandleSTUFF andthen,while
therearestilllinesleftinthefile,printthelinenumber$. andthecurrentline.
Anunforgivableerror
Itfails.That'snotsobad,everythingfailssometimes.WhatisunforgivableisNOTCHECKINGTHEERRORCODE!
Thisisabetterversion:
open STUFF, $stuff or die "Cannot open $stuff for read :$!"
Iftheopen operationfails,theor meansthatthecodeontheRHS(righthandside)isevaluated.Perldies.Thismeansitexitsthe
script,performsapostmortemwhichitwritesupinto$!andtellsyouthelinenumberatwhichitdied.Justbecause$! contains
usefulinformationdoesn'tmeantosayitisautomagicallyprinted,intrueperlfashion.Usuallyyouwillwishtoavailyourselfofthe
informationinsideasitisofgreathelpwhenworkingoutwhysomethingisnotgoingaccordingtoplan.Themoralofthechapteris:
Alwayscheckyourreturncodes!
\\or/inpathnamesyourchoice
Theproblemshouldnowbeapparent.Thebackslashes,beingescapecharacters,arenotdisplayed.Therearetwowaystofixthis:
Escapethebackslashes,likeso$stuff="c:\\scripts\\stuff.txt"
Convertbackslashesintoforwardslashes:$stuff="c:/scripts/stuff.txt"
Theforwardslashesarethepreferredoption,evenunderWin32,becauseyoucanthenportthescriptdirecttoUnixorother
platforms(assumingyoudon'tusedriveletters),anditislesstyping.IfyouwishtousePerltostartexternalprocessesthenyou
mustusethe\\ method,butthisvariablewillbeusedonlyinaPerlprogram,notasaparametertostartanexternalprogram.
Changingthe$stuff variableresultsinaworkingscript.Alwayscheckyourreturncodes!
Readingafile
$stuff="c:/scripts/stuff.txt"
4/10/12 Robert's Perl Tutorial
48/86 www.sthomas.net/roberts-perl-tutorial.htm
open STUFF, $stuff or die "Cannot open $stuff for read :$!"
while (<STUFF>) {
print "Line $. is : $_"
}
Alittlemoredetailonwhatishappeninghere.Thefileisopenedforread.Youcanappendandwritetoo.Youdon'thaetousea
variable,butIalwaysdobecauseitistheneasytochangeandeasytoinsertintotheor die section,anditiseasytochangelater
on.Hardcodingthingsisnotthebestwaytowriteamaintainableandflexibleprogram.JustasktheYear2000peopleaboutcode
thatlivedalittlelongerthantheauthorsimagined:).
open STUFF, "c:/scripts/stuff.txt" or die "Cannot open stuff.txt for read :$!"
isjustasgoodbutmoreworkifyouwanttochangeanything.
Thelineinputoperator(that'stheanglebrackets<> readsfromthebeginningofthefileupuntilandincludingthefirstnewline.The
readdatagoesinto$_ ,andyoucandowhatyouwantwithitthere.Onthenextiterationoftheloopdataisreadfromwherethe
lastreadleftoff,uptothenextnewline.Andsoonuntilthereisnomoredata.Whenthathappenstheconditionisfalseandthe
loopterminates.That'sthedefaultbehaviour,butwecanchangethis.
Thismeansthatyoucanopena200Mbfileinperlandrunthroughitwithouthavingtoloadtheentirefileintomemory.200Mbof
memoryisquiteabit.Ifyoureallywanttoloadtheentire200Mbfileintoonevariable,Perlletsyou.LimitsarenotthePerlWay.
Thespecialvariable$. isthecurrentlinenumber,startingat1.
Asusual,thereisaquickerwaytodothepreviousprogram.
$STUFF="c:/scripts/stuff.txt"
open STUFF or die "Cannot open $STUFF for read :$!"
while (<STUFF>) {
print "Line $. is : $_"
}
Thissavesalittlebitoftyping,butdoestieyourfilehandletothevariablename.Infact,thatentireprogramcouldbecompressed
further,butthat'sforlater.
Ifyouarereallyintoshortness,trythis:
$STUFF="c:/scripts/stuff.txt"
open STUFF or die "Cannot open $STUFF for read :$!"
print "Line $. is : $_" while (<STUFF>)


WritingtoaFile
Asimplewrite
$out="c:/scripts/out.txt"
open OUT, ">$out" or die "Cannot open $out for write :$!"
for $i (1..10) {
print OUT "$i : The time is now : ",scalar(localtime),"\n"
}
Notetheadditionof> tothefilename.Thisopensitforwriting.Ifwewanttoprinttothefilewenowjustspecifythefilehandle
name.Youprinttothefilehandle,whichisagatewaytothefile.
Filehandlesdon'thavetobecapitalised,butitiswise.AllPerlfunctionsarelowercase,andPerliscasesensitive.Soifyou
4/10/12 Robert's Perl Tutorial
49/86 www.sthomas.net/roberts-perl-tutorial.htm
chooseuppercasenamestheyareguaranteednottoconflictwithcurrentorfuturefunctionwords.
Andaneatwaytograbthedatesneakedintheretoo.Youshouldbeawarethatwritingtoafileoverwritesthefile.Itdoesnot
appenddata!However,youmayappend:
Appending
$out="c:/scripts/out.txt"
&printfile
open OUT, ">>$out" or die "Cannot open $out for append :$!"
print OUT 'The time is now : ',scalar(localtime),"\n"
close OUT
&printfile
sub printfile {
open IN, $out or die "Cannot open $out for read :$!"
while (<IN>) {
print
}
close IN
}
Thisscriptdemonstratessubroutinesagain,andhowtoappendtoafile,thatiswriteadditionaldataattheend.Theclose
functionisintroducedhere.This,well,closesafilehandle.Youdon'thavetocloseafilehandlejustleaveitopenuntilthescript
finishes,orthenextopencommandtothesamefilehandlewillcloseitforyou.
@ARGV:CommandLineArguments
Perlhasaspecialarraycalled@ARGV .Thisisthelistofargumentspassedalongwiththescriptnameonthecommandline.Run
thefollowingperlscriptas:
perl myscript.pl hello world how are you
foreach (@ARGV) {
print "$_\n"
}
Anotherusefulwaytogetparametersintoaprogramthistimewithoutuserinput.Therelevancetofilehandlesisasfollows.Run
thefollowingperlscriptas:
perl myscript.pl stuff.txt out.txt
while (<>) {
print
}
Shortandsweet?Ifyoudon'tspecifyanythingintheanglebrackets,whateverisin@ARGV isusedinstead.Andafteritfinisheswith
thefirstfile,itwillcarryonwiththenextandsoon.You'llneedtoremovenonfileelementsfrom@ARGV beforeyouusethis.
Itcanbeshorterstill:
perl myscript.pl stuff.txt out.txt
print while <>
Readitrighttoleft.Itispossibletoshortenitevenfurther!
perl myscript.pl stuff.txt out.txt
print <>
Thistakesalittleexplanation.Asyouknow,manythingsinPerl,includingfilehandles,canbeevaluatedinlistorscalarcontext.The
resultthatisreturneddependsonthecontext.
4/10/12 Robert's Perl Tutorial
50/86 www.sthomas.net/roberts-perl-tutorial.htm
Ifafilehandleisevaluatedinscalarcontext,itreturnsthefirstlineofwhateverfileitisreadingfrom.Ifitisevaluatedinlistcontext,it
returnsalist,theelementsofwhicharethelinesofthefilesitisreadingfrom.
Theprint functionisalistoperator,andthereforeevaluateseverythingitisgiveninlistcontext.Asthefilehandleisevaluatedin
listcontext,itisgivenalist!
Whosaidshortissweet?Notmygirlfriend,butthat'sanotherstory.Theshortestscriptsarenotusuallytheeasiesttounderstand,
andnotevenalwaysthequickest.Asidefromknowingwhatyouwanttoachievewiththeprogramfromafunctionalpointofview,
youshouldalsoknowwheteryouarecodingformaximumperformance,easymaintenanceorwhateverbecausechancesthose
goalsmaybetosomeextentmutuallyexclusive.
ModifyingaFilewith$^I
OneofthemostfrequentPerltasksistoopenafile,makesomechangesandwriteitbacktotheoriginalfilename.Youalready
haveenoughknowledgetodothis.Thestepswouldbe:
1. Makeabackupcopyofthefile
2. Openthefileforread
3. Openanewtemporaryfileforwrite
4. Gothroughthereadfile,andwriteitandanychangestothetempfile
5. Whenfinished,closebothfiles
6. Deletetheoriginalfile
7. Renamethetempfiletotheoriginalfilename
Ifyouhavemanagedtogetthisfarandassiduouslyworkthroughtheexamples,theabovewillbechild'splay.Playifyouwant,but
thereisaBetterWay.
Makesureyouhavedatainc:\scripts\out.txt thenrunthis:
@ARGV="c:/scripts/out.txt"
$^I=".bk" # let the magic begin
while (<>) {
tr/A-Z/a-z/ # another new function sneaked in
print # this goes to the temp filehandle, ARGVOUT,
# not STDOUT as usual, so don't mess with it !
}
So,what'shappening?First,[email protected]'[email protected]
shiftedthecodefromthecommandline.
The$^I isaspecialvariable.Youknewthatjustbylookingatit.It'snameistheInplaceEditvariable,andwhenithasavaluethe
effectsare:
1. Thenameofthefiletobeinplacededitedistakenfromthefirstelementof@ARGV.Inthiscase,thatis
c:/scripts/out.txt.Thefileisrenamedtoitsexistingnameplusthevalueof$^I,ieout.txt.bk.
2. Thefileisreadasusualbythediamondoperator<>,placingalineatatimeinto$_.
3. Anewfilehandleisopened,calledARGVOUT,andnoprizesforguessingitisopenedonafilecalledout.txt.Theoriginal
out.txtisrenamed.
4. Theprint printsautomaticallytoARGVOUT,notSTDOUTasitwouldusually.
Attheendoftheoperationyouhaveneatlyeditedthefileandmadeabackup.Ifyoudon'twantabackup,assignanullstringto
$^I butdon'tgocryingonanymailinglistsifyoulosedata.
Theusualmethodofinplaceeditingwouldinvolvejustprintingeverythingbackwhereitcamefromuntilyourregexfindswhatever
needschanging.Youcouldofcourseslurpthewholefileintomemoryandplaywithitthere,whichcouldbealoteasierbutifyou
aredealingwithfilesofmorethanafewmegabytesthisisprobablynotafeasibleapproach.
Nowtakealookatout.txt .Noticehowallcapitallettershavebeentransliteratedintolowercase.Thisisthetr operatorat
work,whichismoreefficientthanregexforchangingsinglecharacters.Butthat'sonlyasmallpartofthetr function'svalue
totheworld.Morelater.
Youshouldalsohaveanout.txt.bk file.Andfinally,noticetheway@ARGV hasbeencreated.Youdon'thavetocreateitfromthe
commandlineargumentsitcanbetreatedlikeanordinaryarray,forthatiswhatitis.
$/Changingwhatisreadinto$_
4/10/12 Robert's Perl Tutorial
51/86 www.sthomas.net/roberts-perl-tutorial.htm
Onadifferentnote,whatifyourinputfileisdoesn'tlooklikethis:
Beer
Wine
Pizza
Catfood
whichisnicelydelimitedwithanewlineeachtime,butlikethis:
shorts
t-shirt
blouse
pizza
beer
wine
catfood
Viz
Private Eye
The Independent
Byte
toothpaste
soap
towel
whichisdelimitedbyTWOnewlines,notone.Youdon'thavetosavetheaboveasshop.txt,butifyoudon't,theexampleswillbe
difficulttofollow.
Now,ifyouwanteachsetofitemsaselementsinanarrayyou'llhavetodosomethinglikethis:
$SHOP="shop.txt"
$x=0
open SHOP or die "Can't open $SHOP for read: $!\n"
while (<SHOP>) {
if (/^\n/) { # does line begin with newline ?
$x++ # if so, increment $x. Rest of if statement not executed.
} else {
$list[$x].=$_ # glue $_ on the end of whatever is in $list[$x], using a .
}
}
foreach (@list) {
print "Items are:\n$_\n\n"
}
whichworks,butthereisamucheasierwaytodoit.YouknewIwasgoingtosaythat.
$SHOP="shop.txt"
$/="\n\n"
open SHOP or die "Can't open $SHOP for read: $!\n"
while (<SHOP>) {
push (@list, $_)
}
foreach (@list) {
print "Items are:\n$_\n\n"
}
The$/ variableisaspecialvariable(itevenlooksspecial).ItistheDefaultInputRecordSeparator.Remembertheoperationof
theanglebracketsbeingtoreadafileinupuntilthenextnewline?Timetocomeclean.Whattheanglebracketactuallydoisread
upuntilwhatever$/ issetto.Itissettoanewlinebydefault.
4/10/12 Robert's Perl Tutorial
52/86 www.sthomas.net/roberts-perl-tutorial.htm
Soifwesetittotwonewlines,asabove,thenitreadsupuntilitfindstwoconsecutivenewlines,thenputsthedatainto$_ This
makestheprogramalotshorterandquicker.Youcanset$/ tojustaboutanything,notjustanewline.Ifyouwanttohackthislist
forexample:
Tea:Beer:Wine:Pizza:Catfood:Coffee:Chicken:Salmon:Icecream
youcouldjustleave$/ asanewlineandslurpitintomemoryinonego,butimaginetheaboveitemsarealistofclothesthatyour
girlfriendwantstobuyoralistofclothesyourboyfriendshouldhavethrownawaybynow.Eitheraregoingtobereallybigfiles,and
youmightnotwanttoreaditallintomemoryinonego.Soset$/=":" andallwillbewell.Therearealsoread andseek
functions,buttheyaren'tcoveredhere.Thoseareusefulforfileswhereyoureadinaprecisenumberofbytes.
We'llgobacktothelastexampleforamoment.Itisusefultoknowhowtoreadjustoneline(well,upto$/ )atatime:
$SHOP="shop.txt"
$/="\n\n"
open SHOP or die "Can't open $SHOP for read: $!\n"
$clothes=<SHOP> # everything up until the first occurrence of $/ into $clothes
$food=<SHOP> # everything from first occurrence of $/ to the second into $food
print "We need...\n",$clothes,"...and\n",$food
Andnowweknowthat,thereisaevenquickerwaytoachievetheaimoftheoriginalprogram:
$SHOP="shop.txt"
$/="\n\n"
open SHOP or die "Can't open $SHOP for read: $!\n"
@list=<SHOP> # dumps *all* of $SHOP into @list, not just one line.
foreach (@list) {
print "Items are:\n$_\n\n"
}
andyoudon'tneedtograbitall:
@list[0..2]=<SHOP>
Wehaven'tmentionedlistcontextforawhile.Whetherthelineinputoperator<> returnsasinglevalueoralistdependsonthe
contextyouuseitin.Whenyousupply@xxxxx thenthismustbealist.Ifyousupply$xxxxx thenthat'sascalarvariable.Youcan
forceitintolistcontextbyusingparens.
Thetwolinesbelowareprovidedsoyoucanpastethemintotheaboveprogram.Theydemonstratehowparensforcelistcontext.
Remembertoreplacetheforeach withsomethingthatprintsthevariables.
($first, $second) = <SHOP>
$first, $second = <SHOP>

HEREDocs
Theproblem:
print "This is a long line of text which might be too long to fit on just one line\n"
print "and I was right, it was too long to fit on one line. In fact, it looks like it\n"
print "might very well take up to FOUR, yes FOUR lines to print. That's four print\n"
print "statements, which takes up even more room. But wait! I'm wrong! It will take\n"
print "FIVE lines to print this statement! Or is that six lines? I'm not sure....\n"
Thesolution:
$var='variable interpolated'
print <<PRT
4/10/12 Robert's Perl Tutorial
53/86 www.sthomas.net/roberts-perl-tutorial.htm
This is a long line of text which might be too long to fit on just one line
and I was right, it was too long to fit on one line. In fact, it looks like
it might very well take up to FOUR, yes FOUR lines to print.
That's four print statements, which takes up even more room. But wait! I'm
wrong! It will take FIVE lines to print this statement! Or maybe six lines?
I'm not sure....but anyway, just to prove this can be $var.
PRT
That'scalleda'here'documentandyoudon'tneedtousePRT,youcanusewhateveryoulikewithinreason.Youdon'tneedtoput
inexplicitnewlines,althoughifyoudotheyperformasusual.Nowyouknowaboutheredocsyoucanstopwearingtheprint
functionoutbycallingiteverycoupleoflines.Youdon'thavetouseheredocstoprinttofiles,justanywhereyou'dnormallyputa
morethanoneprint statement.
ReadingDirectories
Globbing
Forthisexercise,Isuggestcreatinganotherdirectorywhereyouhaveatleasttwotextfilesandtwoormorebinaryfiles.Copya
coupleof.dllfilesfromyourWINDIRdirectoryifyouneedto,thosewilldoforthebinaries,andsaveacoupleofrandomtextfiles.
Sizedoesn'tmatter,inthiscase.
Thenrunthis,givingthedirectoryasthecommandlineargument:
$dir=shift # shifts @ARGV, the command line arguments after the script name
chdir $dir or die "Can't chdir to $dir:$!\n" if $dir
while (<*>) {
print "Found a file: $_\n" if -T
}

Thechdir functionchangesperl'sworkingdirectory.Youshould,asever,testtoseeifitworkedornot.Inthiscaseweonlytry
andchangedirectoryif$diristrue.
The<*> constructreadsallfilesfromagivendirectory,andprintsifitpassesthefiletest-T ,whichreturnstrueifthefileisanon
binary,ietextfile.Youcanbemorespecific:
$dir =shift
$type='txt'
chdir $dir or die "Can't chdir to $dir:$!\n" if $dir
while (<*.$type>) {
print "Found a file: $_\n"
}
likeso.But,thereisabetterwaytoreadfromdirectories.Themethodaboveisratherslowandinflexible.
readdir:Howtoreadfromdirectories
Instead,thereisreaddir .Anotherversionofthepreviousexample:
$dir= shift || '.'
opendir DIR, $dir or die "Can't open directory $dir: $!\n"
while ($file= readdir DIR) {
print "Found a file: $file\n"
}
Thefirstdifferenceisthefirstline,whichessentiallysaysifshift isfalse,then$dir = .,whichisofcoursethecurrentdirectory.
Then,thedirectoryisopenedandwehavethechancetotraptheerror.Itisassignedafilehandle.Thereaddir functionreads
4/10/12 Robert's Perl Tutorial
54/86 www.sthomas.net/roberts-perl-tutorial.htm
eachfileinto$file.Thereisnowhile (<WDIR>) { construct.
Wecanalsoapplythetextfiletest.Runthis,oncewithoutenteringadirectoryandthesecondtimewithenteringadirectorypath
otherthantheonethescriptisin:
$dir= shift || '.'
opendir DIR, $dir or die "Can't open directory $dir: $!\n"
while ($file= readdir DIR) {
print "Found a file: $file\n" if -T $file
}
Firstly,becausethefilenameisnownotin$_wehavetoexplicitlyapplythe-T testtoitwith-T $file.
Whydidthisnotworkthesecondtime?Lookatthecodecarefully.Youaretesting$file.Ifperldoesn'tgetafullyqualified
pathname,itassumesyouarestillinthedirectorythescriptwasrunfrom,orthatofthelastsuccessfulchdir .Notnecessarily
whereyouarereaddir'ingfrom.So,tofixit:
print "Found a file: $dir/$file\n" if -T "$dir/$file"
wherewenowspecifythepathname,bothintheprintoutandinthefiletestitself.The""areusedbecauseotherwiseperltriesto
divide$fileby$dir.
Tryrunningthisonadirectorywithonlyafewfilesinit:
$dir= shift || '.'
opendir DIR, $dir or die "Can't open directory $dir: $!\n"
while ($file= readdir DIR) {
print "Found a file: '$file'\n"
}
Noticethattwofilesarefoundwhichhaveinterestingnames,namely. and.. .Thesetwofilesarethecurrent,andlower
directoryrespectively.Nothingnew,theyhavealwaysbeenthereruntheDOScommanddir ifyoudon'tbelieveme.Youdon't
usuallywanttoknowaboutthem,so:
while ($file= readdir DIR) {
next if $file=~/^\./
print "Found a file: '$file'\n"
}
istheusualworkaround.Youcanusescalarcontexttodumpeverythingtoalistofsomedescription:
$dir= shift || '.'
opendir DIR, $dir or die "Can't open directory $dir: $!\n"
@files=readdir(DIR)
print "@files"
butthatincludesthe.files,soitisbesttoensuretheyaren'tincluded:
@files=grep !/^\./, readdir(DIR)
Wehaven'tmet-T yet,butforthemomentjustrememberitsearchesalistandifitreturnstrue,letsthevariablepass.Inthiscase,
ifitdoesn'tbeginwith.thenthat'struesoitgoesinto@files.
Thereareothercommandsassociatedwithreadingdirectories,whichtellyouwhereinadirectoryyouare,andthenwheretogoto
return.Youshouldbeawareoftheirexistence,becauseyouneverknowwhenyoumightneedthem.Theoneothercommandof
useisclosedir ,whichclosesadirectory.Optional,butrecommendedforclarity.
AssociativeArrays
4/10/12 Robert's Perl Tutorial
55/86 www.sthomas.net/roberts-perl-tutorial.htm
TheBasics
Very,veryuseful.First,aquickrecaponarrays.Arraysareanorderedlistofscalarvariables,whichyouaccessbytheirindex
numberstartingat0.Theelementsinarraysalwaysstayinthesameorder.
Hashesarealistofscalars,butinsteadofbeingaccessedbyindexnumber,theyareaccessedbyakey.Thetablesbelow
illustratethepoint:
@myarray
IndeNo. Vale
0 TheNetherlands
1 Belgium
2 Germany
3 Monaco
4 Spain
%myhash
Ke Vale
NL TheNetherlands
BE Belgium
DE Germany
MC Monaco
ES Spain
Soifwewant'Belgium'from@myarray andalsofrom%myhash ,it'llbe:
print "$myarray[1]"
print "$myhash{'BE'}"
Noticethatthe$ prefixisused,becauseitisascalarvariable.Despitethefactitispartofalist,itisstillascalarvariable.Thehash
syntaxissimplytousebraces{ } insteadofsquarebrackets.
Sowhyusehashes?Whenyouwanttolooksomethingupbyakeyword.Supposewewantedtocreateaprogramwhichreturns
thenameofthecountrywhengivenacountrycode.We'dinputES,andtheprogramwouldcomebackwithSpain.
Youcoulddoitwitharrays.Itwouldbemessyhowever.Onepossibleapproach:
1. create@country ,andgiveitvaluessuchas'ES,Spain'
2. Itierateovertheentirearrayand
3. split eachelementofthearray,andcheckthefirstresulttoseeifitmatchestheinput
4. Ifso,returntheindex
@countries=('NL,The Netherlands','BE,Belgium','DE,Germany','MC,Monaco','ES,Spain')
print "Enter the country code:"
chop ($find=<STDIN>)
foreach (@countries) {
($code,$name)=split /,/
if ($find=~/$code/i) {
print "$name has the code $code\n"
}
}
Complexandslow.Wecouldalsostoreareferencetoanotherarrayineachelementof@countries ,butthatisnotefficient.
Whateverwaywechoose,youstillneedtosearchthewholething.Andwhatif@countries isabigarray?Seehowmucheasier
ahashis:
AHashinAction
%countries=('NL','The Netherlands','BE','Belgium','DE','Germany','MC','Monaco','ES','Spain')
print "Enter the country code:"
chop ($find=<STDIN>)
$find=~tr/a-z/A-Z/
print "$countries{$find} has the code $find\n"
Veryeasy.Allweneedtodoismakesureeverythingisinuppercasewithtr andwearethere.Noticetheway%countries is
definedexactlythesameasanormalarray,exceptthatthevaluesareputintothehashinkey/valuepairs.
4/10/12 Robert's Perl Tutorial
56/86 www.sthomas.net/roberts-perl-tutorial.htm
Whenyoushouldusehashes
Sowhyusearrays?Oneexcellentreasonisbecausewhenanarrayiscreated,itsvariablesstayinthesameorderyoucreated
themin.Withahash,perlreorderselementsforquickaccess.Addprint %countries totheendofthatprogramaboveandrun
it.SeewhatImean?Norecognisablesequenceatall.It'sliketryingtoherdcats.Ifyouwerewritingcodethatstoredalistof
variablesovertimeandyouwanteditbackintheorderyoufounditin,don'tuseahash.
Finally,youshouldknowthateachkeyofahashmustbeunique.Standstoreason,ifyouthinkaboutit.Youareaccessingthe
hashviakeys,sohowcanyouhavetwokeysnamed'NL'orsomething?Ifyoudodefineacertainkeytwice,thesecondvalue
overwritesthefirst.Thisisafeature,anduseful.Thevaluesofahashcanbeduplicates,butneverthekeys.
Ifyouwanttoassigntoahash,thereisofcoursenoconceptofpush ,pop andsplice etc.Instead:
HashHackingFunctions
Assigning $countries{PT}='Portugal'
Deleting delete $countries{NL}
AccessingYourHash
Assumingyoukeepthesame%countries hashasabove,herearesomeusefulwaystoaccessit:
Allthekeys print keys %countries
Allthevalues print values %countries
ASliceofHash:) print @countries{'NL','BE'}
Howmanyelements? print scalar(keys %countries)
Doesthekeyexist? print "It's there !\n" if exists $countries{'NL'}
Well,thatlastoneisnotanaccessasasuchbutusefulanyway.
MoreHashAccess:Iteration,keysandvalues
Youmayhavenoticedthatkeys andvalues returnalist.Andwecaniterateoveralist,usingforeach:
foreach (keys %countries) {
print "The key $_ contains $countries{$_}\n"
}
whichisuseful.Notehowanylistcanbefedtoforeach ,andoffitgoes.Asusual,thereisanotherwaytodotheabove:
while (($code,$name)=each %countries) {
print "The key $code contains $name\n"
}
Theeach functionreturnseachkey/valuepairofthehash,andisslightlyfaster.Inthisexampleweassignthemtoalist(you
spottedtheparens?)andawaywego.Eventuallytherearenomorepairs,whichreturnsfalsetothewhile loopanditstops.
Ifyouareintobrevity,boththeabovecanbeaccomplishedinasingleline:
print "The key $code contains $name\n" while ($code,$name)=each %countries
print "The key $_ contains $countries{$_}\n" foreach keys %countries

Notethiswon'twinanyprizesforeasilyreadablecodebynonprogrammersofPerl.
4/10/12 Robert's Perl Tutorial
57/86 www.sthomas.net/roberts-perl-tutorial.htm
Sorting
ASimpleSort
IfIwasreadingthisI'dbewonderingaboutsorting.Wondernomore,andbehold:
foreach (sort keys %countries) {
print "The key $_ contains $countries{$_}\n"
}
Spotthedifference.Yes,sort creptinthere.Ifyouwantthelistsortedbackwards,somecunningiscalledfor.Thisissuitablyfoxy:
foreach (reverse sort keys %countries) {
print "The key $_ contains $countries{$_}\n"
}
Perlisjustsodifficultattimes,don'tyouthink?Thisworksbecause:
keysreturnsalist
sortexpectsalistandgetsonefromkeys,andsortsit
reversealsoexpectsalist,soitgetsoneandreturnsit
thenthewholelistisforeach'dover.
Thisisaquickexampletomakesurethemeaningofreverseisclear:
print "Enter string to be reversed: "
$input=<STDIN>
@letters=split //,$input # splits on the 'nothings' in between each character of $input
print join ":", @letters # joins all elements of @letters with \n, prints it
print reverse @letters # prints all of @letters, but sdrawkcab )-:
Perl'slistoperatorscanjustfeeddirectlytoeachother,savingmanylinesofcodebutalsodecreasingreadabilitytothosethat
aren'tPerlliterate:
print "Enter string to be reversed: "
print join ":",reverse split //,$_=<STDIN>
Thissectionisaboutsorting,soenoughofreverse.Timetogoforwardsinstead.
NumericSortingHowSortReallyWorks
That'seasyalphabeticalsortingbythekeys.Ifyouhadahashofinternationalaccessnumberslikethisone:
%countries=('976','Mongolia','52','Mexico','212','Morocco','64','New Zealand','33','France')
foreach (sort keys %countries) {
print "The key $_ contains $countries{$_}\n"
}
Youmightwanttosortnumerically.Inthatcase,youneedtounderstandhowPerl'ssortfunctionworks.
Thesortfunctioncomparestwovariables,$aand$b.Theymustbecalled$aand$botherwiseitwon'twork.Onechappublished
abookwithstolencode,andhechanged$aand$bto$xand$y.Heobviouslydidn'ttesttheprogrambecauseitwouldhavefailed
andhewouldhavenoticed.Andthisbookwasreallypublished!Don'tbelieveeverythingyoureadinbooksbutwebtutorialsare
always100%truthful:)
Backtosorting.$aand$barecompared,andtheresultis:
1if$aisgreaterthan$b
1if$bisgreaterthan$a
0if$aand$bareequal
Soaslongasthesortfunctiongetsoneofthosethreevaluesbackitishappy.Thismeanswecanwriteourownsortroutines,and
feedthemtosort.Forexample,weknowthedefaultsortisalphabetical.Butifwewritethis:
4/10/12 Robert's Perl Tutorial
58/86 www.sthomas.net/roberts-perl-tutorial.htm
%countries=('976','Mongolia','52','Mexico','212','Morocco','64','New Zealand','33','France')
foreach (sort supersort keys %countries) {
print "$_ $countries{$_}\n"
}
sub supersort {
if ($a > $b) {
return 1
} elsif ($a < $b) {
return -1
} else {
return 0
}
}
thenitworkscorrectly.Ofcourse,thereisaneasierway.The'spaceship'operator<=> .Itdoesexactlywhatthesupersort
subroutinedoes,namelyreturn1,1or0dependingonthecomparisonoftwogivenvalues.
Sowecanwritetheabovemuchmoreeasilyas:
%countries=('976','Mongolia','52','Mexico','212','Morocco','64','New Zealand','33','France')
foreach (sort { $a <=> $b } keys %countries) {
print "$_ $countries{$_}\n"
}
Noticethe{}braces,whichdefinethecontentsasthesubroutinesortmustuse.Prettyshortsubroutine.Thereisacompanion
operatorto<=> ,namelycmp whichdoesexactlythesamethingbutofcoursecomparesthevaluesasstrings,not
numbers.Remembe,ifoaecompaingnmbe,ocompaionopeaoholdconainnonalpha,ifoaecompaing
ingheopeaoholdconainalphaonl.Anddon'alkoange.
Anyway,younowhaveenoughknowledgetosortahashbyvalueinsteadofkeys.Supposeyourpointyhairedmanagerbounced
uptoyouanddemandedahashsortedbyvalue?Whatwouldyoudo?OK,whatholdyoudo?
Well,wecouldjustsortthevalues.
foreach (sort values %countries) {
ButPointyHairwantsthekeystoo.Andifyouhaveavalueyoucan'tfindthekey.
Sowehavetoiterateoverthekeys.Butjustbecauseweareiteratingoverthekeysdoesn'tmeantosaywehavetohandthekeys
overtosort .Whatabout:
%countries=('976','Mongolia','52','Mexico','212','Morocco','64','New Zealand','33','France')
foreach (sort { $countries{$a} cmp $countries{$b} } keys %countries) {
print "$_ $countries{$_}\n"
}
beautifullysimple.Ifyouwantareversesorttranspose$a and$b .
SortingMultipleLists
Youcansortseverallistsatthesametime:
%countries=('976','Mongolia','52','Mexico','212','Morocco','64','New Zealand','33','France')
@nations=qw(China Hungary Japan Canada Fiji)
@sorted= sort values %countries, @nations
foreach (@nations, values %countries) {
print "$_\n"
}
print "#----\n"
4/10/12 Robert's Perl Tutorial
59/86 www.sthomas.net/roberts-perl-tutorial.htm
foreach (@sorted) {
print "$_\n"
}
Thissorts@nations andthevaluesfrom%countries intoanewarray.
Theexamplealsodemonstratesthatyoucanforeachovermorethanonelistvalueeachlistisprocessedinturn.HowI
discoveredthatparticulartrickwithPerlisinstructive.Ijusttriedit.IfyouthinkyoushouldbeabletodosomethingwithPerl,tryit.
Adheretothesyntaxandconventionsyouwillbefamiliarwithfromexperience,inthiscasedelimitingalistwithcommas,andtryit.
I'malwaysfindingnewshortcutsjustbyexperimentation.
GrepandMap
Grep
Ifyouwanttosearchalist,andcreateanotherlistofthingsyoufound,grep isonesolution.Thisisanexample,whichalso
demonstratesjoin again:
@stuff=qw(flying gliding skiing dancing parties racing) # quote-worded list
@new = grep /ing/, @stuff # Creates @new, which contains elements of @stuff
# matching with 'ing' in them.
print join ":",@stuff,"\n" # first makes one string out of the elements of @stuff, joined
# with ':' , then prints it, then prints \n
print join ":",@new,"\n"
Rememberqw means'quotewords',sowordboundariesareusedasdelimitersinstead.Thegrep functionmustbefedaliston
therighthandside.Ontheleftside,youmayassigntheresultstoalistorascalarvariable.Assigningtoalistgivesyoueachactual
element,andtoascalargivesyouthenumberofmatchesfound:
@stuff=qw(flying gliding skiing dancing parties racing)
$new = grep /ing/, @stuff
print join ":",@stuff,"\n"
print "Found $new elements of \@stuff which matched\n"
Ifyoudecidetomodifytheelementsontheirwaythroughgrep ,youactuallymodifytheoriginallist.Becarefuloutthere.
@stuff=qw(flying gliding skiing dancing parties racing)
@new = grep s/ing//, @stuff
print join ":",@stuff,"\n"
print join ":",@new,"\n"
Todeterminewhatactuallymatchesyoucaneitheruseanexpressionorablock.Uptonowwe'vebeenusingexpressions,but
whenthingsbecomemorecomplicateduseablock:
@stuff=qw(flying gliding skiing dancing parties racing)
@new = grep { s/ing// if /^[gsp]/ } @stuff
print join ":",@stuff,"\n"
print join ":",@new,"\n"
Tryremovingthebracesandyou'llgetanerror.Noticethatthecommabeforethelisthasgone.Itisnowobviouswherethe
expressionends,asitisinsideablockdelimitedwith{}.Theregexsaysiftheelementbeginswithg,sorp,thenremoveing.The
resultisonlyassignedto@new iftheexpressioniscompletelytrue'parties'doesbeginwithp,sothatworks,buts/ing// failsso
4/10/12 Robert's Perl Tutorial
60/86 www.sthomas.net/roberts-perl-tutorial.htm
theoverallresultisfalse,andthevalueisnotassignedto@new .
Map
Mapworksthesamewayasgrep ,inthattheybothiterateoveralist,andreturnalist.Therearetwoimportantdifferences
however:
grep returnsthevalueofeverythingitevaluatestobere
map returnstheresultsofeverythingitevaluates.
Asusual,anexamplewillassistthepennyindropping,clearthefogandturnonthelight(ifnotmakemymetaphorseasierto
understand):
@stuff=qw(flying gliding skiing dancing parties racing)
print "There are ",scalar(@stuff)," elements in \@stuff\n"
print join ":",@stuff,"\n"
@mapped = map /ing/, @stuff
@grepped = grep /ing/, @stuff
print "There are ",scalar(@stuff)," elements in \@stuff\n"
print join ":",@stuff,"\n"
print "There are ",scalar(@mapped)," elements in \@mapped\n"
print join ":",@mapped,"\n"
print "There are ",scalar(@grepped)," elements in \@grepped\n"
print join ":",@grepped,"\n"
Youcanseethat@mapped isjustalistof1's.Noticethatthereare5oneswhereastherearesixelementsintheoriginalarray,
@stuff.Thisisbecause@mappedcontainstheeelofmap ineverycasetheexpression/ing/ issuccessful,exceptfor
'parties'.
Inthatcasetheretheexpressionisfalse,sotheresultisdiscarded.Contrastthisactionwiththegrep function,whichreturnsthe
actualvalue,butonlyifitistrue.Trythis:
@letters=(a,b,c,d,e)
@ords=map ord, @letters
print join ":",@ords,"\n"
@chrs=map chr, @ords
print join ":",@chrs,"\n"
Thisusestheord functiontochangeeachletterintoitsASCIIequivalent,thenthechr functionconvertASCIInumbersto
characters.Ifyouchangemap togrep intheexampleabove,youcanseethatnothingappearstohappen.Whatishappeningis
thatgrep istryingtheexpressiononeachelement,andifitsucceeds(istrue)itreturnstheelement,nottheresult.Theexpression
succeedsforeachelement,soeachelementisreturnedinturn.Anotherexample:
@stuff=qw(flying gliding skiing dancing parties racing)
print join ":",@stuff,"\n"
@mapped = map { s/(^[gsp])/$1 x 2/e } @stuff
@grepped = grep { s/(^[gsp])/$1 x 2/e } @stuff
print join ":",@stuff,"\n"
print join ":",@mapped,"\n"
print join ":",@grepped,"\n"
Recappingonregex,whatthatdoesismatchanyelementbeginningwithg,sorp,andreplaceitwiththesameelementtwice.The
caret^ forcesamatchatthebeginningofthestring,the[squarebrackets]denoteacharacterclass,and/e forcesPerlto
evaluatetheRHSasanexpression.
Theoutputfromthisisamixtureof1andnothingformap ,andathreeelementarraycalled@grepped fromgrep.Yetanother
example:
4/10/12 Robert's Perl Tutorial
61/86 www.sthomas.net/roberts-perl-tutorial.htm
@mapped = map { chop } @stuff
@grepped = grep { chop } @stuff
Thechop functionremovesthelastcharacterfromastring,andreturnsit.Sothat'swhatyougetbackfrom^ ,theelofthe
expression.Thegrepfunctiongivesyouthemangledremainsoftheoriginalvalue.
Writingyourowngrepandmapfunctions
Finally,youcanwriteyourownfunctions:
@stuff=qw(flying gliding skiing dancing parties racing)
print join ":",@stuff,"\n"
@mapped = map { &isit } @stuff
@grepped = grep { &isit } @stuff
print join ":",@mapped,"\n"
print join ":",@grepped,"\n"
sub isit {
($word)=/(^.*)ing/
if (length $word == 3) {
return "ok"
} else {
return 0
}
}
Thesubroutineisit firstgrabseverythingupuntil'ing',putsitinto$word ,thenreturns'ok'ifthetherearethreecharactersin
$word .Ifnot,itreturnsthefalsevalue0.Youcanmakethesesubroutines(thinkofthemasfunctions)ascomplexasyoulike.
Sometimesitisveryusefultohavemap returntheactualvalue,ratherthantheresult.Theansweriseasy,butnotobvious.
Rememberthatsubroutinesreturnthevalueofthelastexpressionevaluated?So,inthiscase,doblocks.Whatiftheexpression
was,verysimply:
@grepstuff=@mapstuff=qw(flying gliding skiing dancing parties racing)
print join " ",map { s/(^[gsp])/$1 x 2/e } @mapstuff
print "\n"
print join " ",grep { s/(^[gsp])/$1 x 2/e } @grepstuff
Now,makesure$_ isthelastthingevaluated:
@grepstuff=@mapstuff=qw(flying gliding skiing dancing parties racing)
print join " ",map { s/(^[gsp])/$1 x 2/e$_} @mapstuff
print "\n"
print join " ",grep { s/(^[gsp])/$1 x 2/e } @grepstuff
andthereyouhaveit.Nowyouunderstandthatyoucangoandimpressyourfriends,butpleasedon'tcountonsuccess.
ExternalCommands
Somewaysto...
Perlcanstartexternalcommands.Therearefivemainwaystodothis:
system
exec
CommandInput,alsoknownas`backticks`
Pipingdatafromaprocess
Quoteexecute
4/10/12 Robert's Perl Tutorial
62/86 www.sthomas.net/roberts-perl-tutorial.htm
We'llcomparesystemandexecfirst.
Exec
Pooroldexec isbrokenonPerlforWin32.WhatitshoulddoisstoprunningyourPerlscriptandstartrunningwhateveryoutellit
to.Ifitcan'tstarttheexternalprocess,itshouldreturnwithanerrorcode.Thisdoesn'tworkproperlyunderPerlforWin32.The
exec functiondoesworkproperlyonthestandardPerldistribution.
System
Thisrunsanexternalcommandforyou,thencarriesonwiththescript.Italwaysreturns,andthevalueitreturnsgoesinto$? .
Thismeansyoucantesttoseeiftheprogramworked.Actuallyyouaretestingtoseeifitcouldbestarted,whattheprogramdoes
whenitrunsisoutsideyourcontrolifyouusesystem .
Thisexampledemonstratessystem inaction.Runthe'vol'commandfromacommandpromptfirstifyouarenotfamiliarwithit.
Thenrunthe'vole'command.I'massumingyouhavenocutefurryexecutablescalledvoleonyoursystem,oratleastinthepath.
Ifyoudohaveanexecutablecalled'vole',becreativeandchangeit.
system("vole")
print "\n\nResult: $?\n\n"
system("vol")
print "\n\nResult: $?\n\n"
Asyoucansee,asuccessfulsystemcallreturns0.Anunsuccessfulonereturnsavaluewhichyouneedtodivideby256togetthe
realreturnvalue.Alsonoticeyoucanseetheoutput.Andbecausesystemreturns,thecodeafterthefirstsystem callisexecuted.
Notsowithexec,whichwillterminateyourperlscriptifitissuccessful.Perl'susualuseofsingleanddoublequotesappliesasper
variableinterpolation.
Backticks
These`` aredifferentagaintosystemandexec.Theyalsostartexternalprocesses,butreturntheoutputoftheprocess.You
canthendowhateveryoulikewiththeoutput.Ifyouaren'tsurewherebackticksareonyourkeyboard,trythetopleft,justleftof
the1key.Oftenaroundthere.Don'tconfusesinglequotes'' withbackticks`` .
$volume=`vol`
print "The contents of the variable \$volume are:\n\n"
print $volume
print "\nWe shall regexise this variable thus :\n\n"
$volume=~m#Volume in drive \w is (.*)#
print "$1\n"
Asyoucanseehere,theWin32volcommandisexecuted.Wejustprintitout,escapingthe$ inthevariablename.Thenasimple
regex,using#asadelimiterjustincaseyou'dforgottendelimitersdon'thavetobe/.
Whentouseexternalcalls
BeforeyougetcarriedawaywithcreatingelaboratescriptsbasedontheoutputfromNT'snet commands,notethereareplentyof
excellentmodulesouttherewhichdoaverygoodjobofthissortofthing,andthatanyformofexternalprocesscallslowsyour
script.Alsonotethereareplentyofbuiltinfunctionssuchasreaddir whichcanbeusedinsteadof`dir` .YoushouldusePerl
functionswherepossibleratherthancallingexternalprogramsbecausePerl'sfunctionsare:
portable(usually,butthereareexceptions).ThismeansyoucanwriteascriptonyourMacPowerBook,testitonanNTbox
andthenuseitliveonyourUnixboxwithoutmodifyingasinglelineofcode
faster,aseveryexternalprocesssignificantlyslowsyourprogram
don'tusuallyrequireregexingtofindtheresultyouwant
don'trelyonoutputinaparticularformat,whichmightbechangedinthenextversionofyourOSorapplication
4/10/12 Robert's Perl Tutorial
63/86 www.sthomas.net/roberts-perl-tutorial.htm
aremorelikelytobeunderstoodbyaPerlprogrammerforexample,$files=`ls` onaUnixboxmeanslittleto
someonethatdoesn'tknowthatlsistheUnixcommandforlistingfiles,asdirisinWindows.
Don'tstartusingbackticksallovertheplacewhensystemwilldo.Youmightgetaverylargereturnvaluewhichyoudon'tneed,
andwillconsequentlyslurplotsofmemory.Justusethemwhenyouactuallywanttocheckthereturnedstrings.
OpeningaProcess
Theproblemwithbackticksisthatyouhavetowaitfortheentireprocesstocomplete,thenanalysetheentirereturncode.Thisisa
bigproblemifyouhavelargereturncodesorslowprocesses.Forexample,theDOScommandtree.Ifyouaren'tfamiliarwiththis
command,runaDOS/commandprompt,switchtotherootdirectory(C:\ )andtypetree.Examinethewondrousoutput.
Wecanopenaprocess,andpipedatainviaafilehandleinexactlythesamewayyouwouldreadafile.Thecodebelowisexactly
thesameasopeningafilehandleonafile,withtwoexceptions:
1. Weuseanexternalcommand,notafilename.That'stheprocessname,inthiscase,tree.
2. Apipe,ie| isappendedtotheprocessname.
open TRIN, "tree c:\\ /a |" or die "Can't see the tree :$!"
while (<TRIN>) {
print "$. $_"
}
Notethe| whichdenotesthatdataistobepipedfomthespecifiedprocess.Youcanalsopipedataoaprocessbyusing| as
thefirstcharacter.
Asusual,$. isthelinenumber.Whatwecandonowisterminateourtreeearly.Environmentallyunsound,butefficient.
open TRIN, "tree c:\\ /a |" or die "Can't see the tree :$!"
while (<TRIN>) {
printf "%3s $_", $.
last if $. == 10
}
Assoonas$. hits10weshuttheprocessoffbyexitingtheloop.Easy.
Except,maybeitwon't.Whatifthiswasalongprogram,andyouforgotaboutthatparticularlineofcodewhichexitstheloop?
Supposethat$. somehowwentfrom9to11,orwasassignedto?Itwouldneverreach10.So,tobesafe
open TRIN, "tree c:\\ /a |" or die "Can't see the tree :$!"
while (<TRIN>) {
printf "%3s $_", $.
last if $. >= 10
}
exityourloopsinaparanoidmanner,unlessyoueallmeanonlytoexitwhenatlineten.Formaximumsafety,maybeyoushould
createyourowncountervariablebecause$. isaglobalvariable.I'mnotnecessarilyadvocatingdoinganyoftheabove,butIam
suggestedthesethingsareconsidered.
Youmightnoticethepresenceofanewkeywordprintf .Itworkslikeprint ,butformatsthestringbeforeprinting.The
formattingiscontrolledbysuchparametersas%3s ,whichmeans"padouttoatotalofthreespaces".Afterthedoublequoted
stringcomeswhateveryouwanttobeprintedintheformatspecified.Someexamplesfollow.Justuncommenteachlineinturnto
seewhatitdoes.Thereisalotofnewstuffbelow,buttryandworkoutwhatishappening.Anexplanationfollowsafterthecode.
$windir=$ENV{'WINDIR'} # yes, you can access the environment variables !
$x=0
opendir WDIR, "$windir" or die "Can't open $windir !!! Panic : $!"
while ($file= readdir WDIR) {
next if $file=~/^\./ # try commenting this line to see why it is there
$age= -M "$windir/$file" # -M returns the age in days
$age=~s/(\d*\.\d{3}).*/$1/ # hmmmmm
4/10/12 Robert's Perl Tutorial
64/86 www.sthomas.net/roberts-perl-tutorial.htm
#### %4.4d - must take up 4 columns, and pad with 0s to make up space
#### and minimum width is also 4
#### %10s - must take up 10 columns, pad with spaces
# printf "%4.4d %10s %45s \n", $x, $age, $file
#### %-10s - left justify
# printf "%4.4d %-10s %-45s \n", $x, $age, $file
#### %10.3 - use 10 columns, pad with 0s if less than 3 columns used
# printf "%4.4d %10.3d %45s \n", $x, $age, $file
$x++
last if $x==15 # we don't want to go through all the files :-)
}
Therearesomeintentionallynewfunctionsthere.WhenyoustarthackingPerl(actually,youalreadystartedifyouhaveworked
throughthisfar)you'llseealotofexamplecode.Tryandunderstandtheabove,thenreadtheexplanationbelow.
Firstly,allenvironmentvariablescanbeaccessedandsetviaPerl.Theyareinthe%ENV hash.Ifyouaren'tsurewhatenvironment
variablesare,refertoyourfriendlyMicrosoftdocumentationorbooks.Thebestknownenvironmentvariableispath,andyoucan
seeitsvalueandthatofallotherenvironmentvariablesbysimplytypingset atyourcommandprompt.
Theregex/^\./ bouncesoutinvalidentriesbeforewebotherdoanyprocessingonthem.Goodprogrammingpractice.Whatit
matchesis"anythingthatbeginswith'.'".Thecaretanchorsthematchtothebeginningofthestring,andas. isametacharacterit
hastobeescaped.
Perlhasseveralteststoapplyonfiles.The-M testreturnstheageindays.Seethedocumentationforsimilartests.Notethatthe
callstoreaddir returnjustthefile,notthecompletepathname.Asyouwerecarefultouseavariableforthedirectorytobe
openedratherthanhardcodingit(horrors)itisnotroubletoglueittogetherbyusingdoublequotes.
Trycommentingout$age=~s/(\d*\.\d{3}).*/$1/ andnotethesizeof$age .Itcoulddowithatrim.Justforregexpractice,
wemakeitalittlesmaller.Whattheregexdoesis:
startcapturingwith(
lookfor0ormoredigits\d*
thena. (escaped)
followedbythreedigits\d{3}
andthat'sallwewanttocapturesotheparensareclosed.)
Finally,everythingelseinthestringismatched.* where. isanycharacter(almost)and* 0ormore.Thisisprettymuch
guaranteedtomatchtotheendoftheline
Havingmatchedtheentirestring(andputpartofitinto$1 byusingparens)wesimplyreplacethestringwithwhatwehave
matched.
Easy!
Mentionshouldalsobemadeofsprintf ,whichisexactlylikeprintf exceptitdoesn'tprint.Youjustuseittoformatstrings,
whichyoucandosomethingwithlater.Forexample:
open TRIN, "tree c:\\ /a |" or die "Can't see the tree :$!"
while (<TRIN>) {
$line= sprintf "%3s $_", $.
print $line
last if $. == 10
}

Quoteexecute
@opts=qw(w on ad oe b)
for (@opts) {
$result=qx(dir /$_)
print "dir /$_ resulted in:\n$result",'-' x 79
sleep 1
4/10/12 Robert's Perl Tutorial
65/86 www.sthomas.net/roberts-perl-tutorial.htm
}
Anythingwithinqx( ) isexecuted,anddulyvariableinterpolated.Thissamplealsodemonstratedqw whichis'quotewords',so
theelementsof@optsaredelimitedbywordboundaries,nottheusualcommas.Youcanalsousefor insteadofforeach ifyou
wanttosavetypingfourcharacterforthesakeoflegibility.
Youmayhavenoticedthatsystem outputstheresultofthecommandtothescreenwhereasqx doesnot.Eachtoitsown.
Oneliners
Ashortexample
You'llhavenoticedPerlpacksalotofpowerintoasmallamountofcode.YoucanfeedPerlcodedirectlyonthecommandline.
Thisisknownasaoneliner,forobviousreasons.Anexample:
perl -e"for (55..75) { print chr($_) }"
The-e switchtellsPerlthatacommandisfollowing.Thecommandmustbeenclosedindoublequotes,notsinglesasonUnix.The
commanditselfinthiscasesimplyprintstheASCIIcodeforthenumber55to75inclusive.
Fileaccess
Thisisasimplefindroutine.Asitusesaregex,itisinfinitelysuperiortoNT'sfindstr :
perl -e"while (<>) {print if /^[bv]/i}" shop.txt
Remember,thewhile (<>) constructwillopenwhateverisin@ARGV .Inthiscase,wehavesuppliedshop.txt soitisopened
andweprintlinesthatbeginwitheither'b'or'v'.
Thatcanbemadeshorter.Runperl -h andyou'llseeawholelistofswitches.Theonewe'llusenowis-n ,whichputsawhile
(<>) { } looparoundwhatevercodeyousupplywith-e .So:
perl -ne"print if /^[bv]/i" shop.txt
whichdoesexactlythesameasthepreviousprogram,butusesthe-n switchtoputawhile (<>) looparoundwhateverother
commandsaresupplied.
Aslightlymoresophisticatedversion:
perl -ne"printf \"$ARGV : %3s : $_\",$. if /^[bv]/i" shop.txt
whichdemonstratesthatdoublequotesmustbeescaped.
Modifyingfileswithaonelinerand$^I
Ifyoudon'tremember$^I thenpleasereviewthesectiononFilesbeforeproceeding.Whenyou'reready,copyshop.txt to
shop2.txt .
perl -i.bk -ne"printf \"%4s : $_\",$." shop2.txt
The-i switchprimestheinplaceeditoperator.Westillneed-n .
Ifyouhadatypicalquotedemailmessagesuchas:
>> this is what was said
>> blah blah
> blaaaaahhh
The new text
4/10/12 Robert's Perl Tutorial
66/86 www.sthomas.net/roberts-perl-tutorial.htm
andyouwantedtoremovethe>,then:
perl -i.bk -pe"s/^>+ ?//" email.txt
doesthetrick.Regexrecapthecaretmatcheswhatfollowstothebeginningofthestring,the+meansoneormore(no,wedo
notuse*whichmeans0ormore),thenwewillmatchonespacewith\s ,butitisnotnecessaryforthespacetobethereforthe
matchtobesuccessful,hence? .
Whatisnewintermsofonelinersistheuseof-p ,whichdoesexactlythesamethingas-n exceptthatitaddsaprint
statementtoo.Incaseyouwerewonderingwhythepreviousexampleused-n andthisoneuses-p thepreviousexampleuses
printsdatawithprintf,whereasthisexampledoesn'thaveanexplicitprintstatementsoweprovideonewith-p .
SomeotherusefulonelinersacalculatorandaASCIInumberlookup:
perl -e"print 50/200+2"
perl -e"for (50..90) { print chr($_) }"
Thereareplentymoreoneliners,andtheyareanessentialpartofanysysadmin'stoolbox.Thetwoexamplesbeloware
functionallyequivalentbuttheloweroneisperhapsalittlemorereadable:
perl -e"for $i (50..90) { print chr($i),\" is $i\n\" }"
perl -e"for $i (50..90) { print chr($i),qq| is $i\n| }
Whateverfollowsqq isusedasadelimiter,insteadofhavingtoescapethebackslash.IlearntthisfromthePerlWin32Users
mailinglist(seetop)IthinkitwasLennartBorgmanwhopointeditout.Healsomentionedthatyoudon'tneedtheclosing
doublequote.Savesalittletyping.
SubroutinesandParameters
InPerl,subroutinesarefunctionsaresubroutines.Ifyoulike,asubroutineisauserdefinedfunction.It'sabitlikecallingascripta
program,oraprogramascript.Forthepurposesofthistutorialwe'llrefertofunctionsassubroutines,exceptwhenwecallthem
functions.Hopethat'smadethepoint.
Forthepurposesofthissectionwewilldevelopasmallprogramwhich,bytheend,willdemonstratehowsubroutineswork.Italso
servestodemonstratehowmanyprogramsarebuilt,namelyalittleatatime,inmanageablesections.Atleast,thatmethodworks
forme.engines.
Thechosenthemeisgliding.That'saeroplaneswithoutengines.Asubjectclosetoeverygliderpilot'sheartishowfartheycanfly
fromthealtitudetheyareat.Ourprogramwillcalculatethis.Tomakeiteasywe'llassumetheairisperfectlycalm.Windwouldbea
complicationwedon'tneed,especiallywheninacrowdedlift.
Whatweneedinordertocalculatethedistancewecanflyis:
Howhighweare(infeet)
Howmanymetreswetravelforwardforeverymetrewedrop.Thisistheglideratio,forexample24:1wouldmeantravelling
24metresforwardforevery1metreofheightlost.
Obviouslyinputisneeded.Wecaneitherprompttheuserorgrabtheinputfromthecommandline.Thelatteriseasiersowe'lljust
lookat@ARGV forthecommandlineparameters.Likeso:
($height,$angle)=@ARGV # @ARGV is the command line parameters
$distance=$height*$angle # an easy calculation
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
Theaboveshouldbeexecutedthus:
perl yourscript.pl 5000 24
orwhateveryourscriptiscalled,withwhateverparametersyouchoosetouse.I'mapoetandIdon'tevenknowit.
Thatworks.Whataboutaslightvariation?Thepilotdoeshavesomecontrolovertheglideratio,forexamplehecanflyfasterbutat
apenaltyofalesserglideratio.Soweshouldperhapsgiveacoupleofoptionseithersideofthegivenparameters:
($height,$angle)=@ARGV
4/10/12 Robert's Perl Tutorial
67/86 www.sthomas.net/roberts-perl-tutorial.htm
$distance=$height*$angle
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
$angle++ # add 1 to $angle
$distance=$height*$angle
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
$angle-=2 # subtract 2 from $angle so it is 1 less than the original
$distance=$height*$angle
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
That'scumbersomecode.Werepeatexactlythesamestatement.Thiswastesspace,andifwewanttochangeittherearethree
changestobemade.Abetteroptionistoputitintoasubroutine:
($height,$angle)=@ARGV
&howfar
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
$angle++
&howfar
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
$angle-=2
&howfar
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
sub howfar { # sub subroutinename
$distance=$height*$angle
}
Thisisabasicsubroutine,andyoucouldstophereandhavelearntaveryusefultechniqueforprogramming.Now,whenchanges
aremadetheyaremadeinoneplace.Lesswork,lesschancesoferrors.Improvementscanalwaysbemade.Forexample,pilots
outsideEasternEuropegenerallymeasureheightinfeet,andgliderpilotsareusuallyconcernedwithhowmanykilometresthey
travelovertheground.Sowecanadaptourprogramtoacceptheightinfeetandoutputthedistanceinkilometres:
($height,$angle)=@ARGV
$height/=3.2 # divide feet by 3.2 to get metres
&howfar
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
$angle++
&howfar
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
$angle-=2
&howfar
print "With a glide ratio of $angle:1 you can fly $distance from $height\n"
sub howfar {
$distance=$height*$angle
}
Whenyourunthisyou'llprobablygetaresultwhichinvolvesafairfewdigitsafterthedecimalpoint.Thisismessy,andwecanfix
thisbytheint function,whichinPerlandmostotherlanguagesreturnsanumberasaninteger,iewithoutthoseirritatingnumbers
afterthedecimalpoint.
YoumighthavealsonoticedasmallbitofBadProgrammingPracticeslippedintothelastexample.ItwastheevilConstant,the
'3.2'usedtoconvertfeettometres.Why,Idon'thearyouask,isthisbad?Surelytheconversionwillneverchange?
Itwon'tchange,butouruseofitmight.Wemaydecidethatitshouldbe3.208insteadof3.2.Wemaydecidetoconvertfromfeet
tonauticalmilesinstead.Youdon'tknowwhatcouldhappen.Therefore,codewithflexibilityinmindandthatmeansavoiding
constants.
Thenewimprovedversionwithint andconstantremoved:
4/10/12 Robert's Perl Tutorial
68/86 www.sthomas.net/roberts-perl-tutorial.htm
($height,$ratio)=@ARGV
$cnv1=3.2 # now it is a variable. Could easily be a cmd line
# parameter too. We have the flexibility.
$height =int($height/$cnv1) # divide feet by 3.2 to get metres
&howfar
print "With a glide ratio of $ratio:1 you can fly $distance from $height\n"
$ratio++
&howfar
print "With a glide ratio of $ratio:1 you can fly $distance from $height\n"
$ratio-=2
&howfar
print "With a glide ratio of $ratio:1 you can fly $distance from $height\n"
sub howfar {
$distance=int($height*$ratio)
}
Wecouldofcoursebuildtheprint statementintothesubroutine,butIusuallyseparateoutputpresentationfromthecalculation.
Again,thatmeansitiseasiertomodifylateron.
Somethingelsewecanimproveaboutthiscodeistheuseofthe$ratio variable.Wearehavingtokeeptrackofwhatwedotoit
firstaddone,thensubtracttwoinordertosubtractonefromtheoriginalinput.Inthiscaseitisfairlyeasy,butwithacomplex
programitcanbedifficult,andyoudon'twanttobecreatinglotsofvariablesjusttotrackoneinput,forexample$ratio1 ,
$ratio2 etc.
Parameters
Onesolutionistopassthesubroutineparameters.InthebesttraditionofAmericancolumnists,whoseemtohaveaparticular
affectionforthisphrase,'Here'show:'
($height,$ratio)=@ARGV
$cnv1=3.2
&howfar($height,$ratio)
print "With a glide ratio of $ratio:1 you can fly $distance from $height\n"
&howfar($height,$ratio+1)
print "With a glide ratio of ",$ratio+1,":1 you can fly $distance from $height\n"
&howfar($height,$ratio-1)
print "With a glide ratio of ",$ratio-1,":1 you can fly $distance from $height\n"
sub howfar {
print "The parameters passed to this subroutine are @_\n"
($ht,$rt)=@_
$ht =int($ht/$cnv1)
$distance=int($ht*$rt)
}
Quiteafewthingshavechangedhere.Firstly,thesubroutineisbeingcalledwithparameters.Theseareacommadelimitedlistin
parensafterthesubroutinecall.Thetwoparametersare$heightand$ratio.
Theparametersendupinthesubroutineasthe@_ array.Beinganarray,theyareinthesameorderaspassed.Alltheusualarray
operationswork.Allwewilldoisassignthecontentsofthearraytotwovariables.
Wehavealsomovedtheconversionfunctionintothesubroutine,becausewewanttoputallthecodeforgeneratingthedistance
intooneplace.
Namespaces
4/10/12 Robert's Perl Tutorial
69/86 www.sthomas.net/roberts-perl-tutorial.htm
Wecannotusethevariablenames$height and$ratio becausewemodifytheminthesubroutineandthatwillaffectthemain
program.Sowechoosenewonestodotheoperationon.Finally,asmallchangeismadetotheprintoutput.
Thisapproachworkswellenoughforoursmallprogramhere.Forlargerprograms,havingtothinkofnewvariablenamesallthe
timeisdifficult.Itwouldbeevenmoredifficultifdifferentprogrammerswereworkingondifferentsectionsoftheprogram.Itwould
beimpossibleifaprogramwerewritten,thenanextensioncreatedbyanotherpersonsomewhereelse,andthatsameextension
hadtobeusedbymanypeopleinmanydifferentprograms.Obviously,theriskofusingthesamevariablenameistoogreat.There
areonlysomanylogicalnamesoutthere.
Thereisasolution.Imagineyouownahousewithtwogardens.Youhavetwoidenticaldogs,oneinthefrontgarden,oneinthe
backgarden.Bearwithme,thisisrelevant.BothdogsarecalledRover,becausetheirownerlacksimagination.
Whenyougotothefrontgardenandcall'Rover!!!'oropenacanofdogfood,thedoginthefrontgardencomesrunning.Similarly,
yougotothebackgarden,callyourdogandtheotherdogbouncesuptoyou.
Youhavetwodogs,bothcalledRover,andyoucanchangeeitheroneofthem.Washone,neutertheotheritdoesn'tmatter,but
botharedogsandbothhavethesamename.Changestoonewon'taffecttheother.Youdon'tgetthemconfusedbecausethey
areindifferentplaces,intwodifferentnamespaces.
VariableScope
TobringthingsbacktoPerl,ashortdiversionisnecessarytoillustratethepointwithactualPerlcodeinsteadofcaninemetaphors:
$name='Rover'
$pet ='dog'
$age =3
print "$name the $pet is aged $age\n"
{
my $age =4 # run this again, but comment this line out
my $name='Spot' # and this one
$pet ='cat'
print "$name the $pet is aged $age\n"
}
print "$name the $pet is aged $age\n"
Thisisprettystraightforwarduntilwegettothe{ .Thismarksthestartofablock.Onefeatureofablockisthatitcanhaveitsown
namespace.Variablesdeclared,inotherwordsinitialised,withinthatblockarejustnormalvariables,unlesstheyaredeclaredwith
my .
Whenvariablesaredeclaredwithmy theyarevisibleinsidetheblockonly.Also,anyvariablewhichhasthesamenameoutsidethe
blockisignored.Pointstonotefromtheexampleabove:
Thetwomy variablesappeartooverwritethevariablesofthesamenamefromoutsidetheblock.
Thetwooriginalvariablesaren'treallyoverwrittenbecauseasweproveaftertheblockhasended,theyhaven'tbeen
touched.
Thevariable$petisaccessibleinsideandoutsidetheblockasusual.Ofcourse,ifwedeclareitwithmy thenthingswill
change.
myVariables
Sotherewehaveit.Namespaces.Theyworkforalltheothertypesofvariabletoo,likearraysandhashes.Thisishowyoucan
writecodeandnotcareaboutwhatotherpeopleuseforvariablenamesyoujustdeclareeverythingwithmy andhaveyourown
privateparty.Ouroriginalprogramaboutglidingcanbeimprovednow:
($height,$ratio)=@ARGV
$cnv1=3.2
&howfar($height,$ratio)
print "With a glide ratio of $ratio:1 you can fly $distance from $height\n"
4/10/12 Robert's Perl Tutorial
70/86 www.sthomas.net/roberts-perl-tutorial.htm
&howfar($height,$ratio+1)
print "With a glide ratio of ",$ratio+1,":1 you can fly $distance from $height\n"
&howfar($height,$ratio-1)
print "With a glide ratio of ",$ratio-1,":1 you can fly $distance from $height\n"
sub howfar {
my ($height,$ratio)=@_
$height =int($height/$cnv1)
$distance=int($height*$ratio)
}
Theonlychangeisthattheparameterstothesubroutine,iethecontentsofthearray@_ ,aredeclaredwithmy .Thismeansthey
arenowonlyvisiblewithinthatblock.Theblockhappenstoalsobeasubroutine.Outsideoftheblock,theoriginalvariablesarestill
accessible.AtthispointI'llintroducethetechnicalterm,whichislexicalscoping.Thatmeansthevariableisconfinedtotheblock
itisonlyvisiblewithintheblock.
Westillhavetobeconcernedwithwhatvariablesweuseinsidethesubroutine.Thevariable$distanceiscreatedinthe
subroutineandusedoutsideofit.Withlargerprogramsthiswillcauseexactlythesameproblemasbeforeyouhavetobecareful
thatthesubroutinevariablesyouusearethesameonesasoutsidethesubroutine.Forallthesamereasonsasbefore,liketwo
differentpeopleworkingonthecodeanduseofcustomextensionstoPerl,thatcanbedifficult.
Theobvioussolutionistodeclare$distancewithmy ,andthuslexicallyscopeit.Ifwedothis,thenhowdowegettheresultof
thesubroutine?Likeso:
($height,$ratio)=@ARGV
$cnv1=3.2
$distance=&howfar($height,$ratio) # run this again and delete '$distance='
print "With a glide ratio of $ratio:1 you can fly $distance from $height\n"
$distance=&howfar($height,$ratio+1)
print "With a glide ratio of ",$ratio+1,":1 you can fly $distance from $height\n"
$distance=&howfar($height,$ratio-1)
print "With a glide ratio of ",$ratio-1,":1 you can fly $distance from $height\n"
sub howfar {
my ($height,$ratio)=@_
my $distance
$height =int($height/$cnv1)
$distance=int($height*$ratio/1000) # output result in kilometres not metres
}
Firstchange$distanceisdeclaredwithmy .Secondly,theresultofthesubroutineisassignedtoavariable,whichisalso
named$distance.However,itisa$distanceinadifferentnamespace.Rememberthetwogardens.Youmaywishtodeletethe
$distance=fromthefirstassignmentandrerunthecode.Theonlyotherchangeisonetochangetheoutputfrommetersto
kilometres.
WehavenowachievedasortofBlackBoxeffect,wherethesubroutineisgiveninputandcreatesoutput.Wepassthesubroutine
twonumbers,whichmayormaynotbevariables.Weassigntheoutputofthesubroutinetoavariable.Wecarenotwhatgoeson
insidethesubroutine,whatvariablesitusesorwhatmagicitperforms.Thisishowsubroutinesshouldoperate.Theonly
exceptionisthevariable$cnv1.Thisisdeclaredinthemainbodyoftheprogrambutalsousedinthesubroutine.Thishasbeen
doneincaseweneedtousethevariableelsewhere.Inlargerprogramsitwouldbeagoodideatopassittosubroutinesalongwith
theotherparameterstoo.
MultipleReturns
That'sallthemajorlearningoutthewaywith.Thenextstepisrelativelyeasy,butweneedtoaddnewfunctionalitytotheprogram
inordertodemonstrateit.Whatwewilldoisworkouthowlongitwilltakethegliderpilottoflythedistance.Forthiscalculation,we
needtoknowhisairspeed.Thatcanbeathirdparameter.Theactualcalculationwillbepartofhowfar.Aneasychange:
($height,$ratio,$airspeed)=@ARGV
$cnv1=3.2
$cnv2=1.8
4/10/12 Robert's Perl Tutorial
71/86 www.sthomas.net/roberts-perl-tutorial.htm
($distance,$time)=&howfar($height,$ratio,$airspeed)
print "Glide ratio $ratio:1, $distance from $height taking $time\n"
($distance,$time)=&howfar($height,$ratio+1,$airspeed)
print "Glide ratio ",$ratio+1,":1, $distance from $height taking $time\n"
($distance,$time)=&howfar($height,$ratio-1,$airspeed)
print "Glide ratio ",$ratio-1,":1, $distance from $height taking $time\n"
sub howfar {
my ($height,$ratio,$airspeed)=@_
my ($distance,$time) # how to 'my' multiple variables
$airspeed*=$cnv2 # convert knots to kmph
$height =int($height/$cnv1)
$distance=int($height*$ratio/1000)
$time =int($distance/($airspeed/60)) # simple time conversion
# print "Time:$time, Distance:$distance\n" # uncomment this later
}
Thisdoesn'tworkcorrectly.First,thechanges.Theresultfromhowfar isnowassignedtotwovariables.Subroutinesreturnalist,
andsoassigningtosomescalarvariablesbetweenparensseparatedbycommaswillwork.Thisisexactlythesameasreadingthe
commandlineargumentsfrom@ARGV .
Wearealsopassinganewparameter,$airspeed.Thereisaanotherconversionandaonelinecalculationtoprovidetheamount
ofminutesitwilltaketofly$distance.
Ifyoulookcarefully,youcanperhapsworkoutwhattheproblemis.TherewasaclueintheRegexsection,when/e was
explained.
TheproblemisthatPerlreturnstheresultofthelastexpressionevaluated.Inthiscase,thelastexpressionistheone
calculating$time,sothevalue$timeisreturned,anditistheonlyvaluereturned.Therefore,thevalueof$timeisassignedto
$distance,and$distanceitselfdoesn'tactuallygetavalueatall.
Reruntheprogrambutthistimeuncommentthelineinthesubroutinewhichprints$distanceand$time.You'llnoticedthevalue
is1,whichmeansthattheexpressionwassuccessful.Perlisfaithfullyreturningthevalueofthelastexpressionevaluated.
Thisisallwellandgood,butnotwhatweneed.WhatisrequiredisamethodoftellingPerlwhatneedstobereturned,ratherthan
whatPerlthinkswouldbeagoodidea:
($height,$ratio,$airspeed)=@ARGV
$cnv1=3.2
$cnv2=1.8
($distance,$time)=&howfar($height,$ratio,$airspeed)
print "Glide ratio $ratio:1, $distance from $height taking $time\n"
($distance,$time)=&howfar($height,$ratio+1,$airspeed)
print "Glide ratio ",$ratio+1,":1, $distance from $height taking $time\n"
($distance,$time)=&howfar($height,$ratio-1,$airspeed)
print "Glide ratio ",$ratio-1,":1, $distance from $height taking $time\n"
sub howfar {
my ($height,$ratio,$airspeed)=@_
my ($distance,$time) # how lexically scope multiple variables
$airspeed*=$cnv2 # convert knots to kmph
$height =int($height/$cnv1)
$distance=int($height*$ratio/1000) # output result in kilometres not metres
$time =int($distance/($airspeed/60)) # simple time conversion
return ($distance,$time) # explicit return
}
Asimplefix.Now,wetellPerlwhattoreturn,withtheaptlynamedreturn function.Withthisfunctionwehavecompletecontrol
overwhatisreturnedandwhen.Itisquiteusualtouseif statementstocontroldifferentreturnvalues,butwewon'tbotherwith
thathere.
Thereisasubtleflawintheprogramabove.Itisnotbackwardscompatiblewiththeoldmethodofcallingthesubroutine.Runthis:
4/10/12 Robert's Perl Tutorial
72/86 www.sthomas.net/roberts-perl-tutorial.htm
($height,$ratio,$airspeed)=@ARGV
$cnv1=3.2
$cnv2=1.8
($distance,$time)=&howfar($height,$ratio,$airspeed)
print "Glide ratio $ratio:1, $distance from $height taking $time\n"
($distance,$time)=&howfar($height,$ratio+1,$airspeed)
print "Glide ratio ",$ratio+1,":1, $distance from $height taking $time\n"
$distance=&howfar($height,$ratio-1) # old way of calling it
print "With a glide ratio of ",$ratio-1,":1 you can fly $distance from $height\n"
sub howfar {
my ($height,$ratio,$airspeed)=@_
my ($distance,$time)
$airspeed*=$cnv2
$height =int($height/$cnv1)
$distance=int($height*$ratio/1000)
$time =int($distance/($airspeed/60))
return ($distance,$time)
}
Adivisionby0resultsthirdtimearound.Thisisofcoursebecause$airspeeddoesn'texist,soofcourseitwilleffectivelybe0.
Makingyoursubroutinesbackwardscompatibleisimportantinlargeprograms,orifyouarewritinganaddinmoduleforother
peopletouse.Youcan'texpecteveryonetoretrofitadditionalparameterstotheirsubroutinecallsjustbecauseyoudecidedtobea
bitcreativeoneday.
Therearemanywaystofixtheproblem,andthisisjustone:
($height,$ratio,$airspeed)=@ARGV
$cnv1=3.2
$cnv2=1.8
($distance,$time)=&howfar($height,$ratio,$airspeed)
print "Glide ratio $ratio:1, $distance from $height taking $time\n"
($distance,$time)=&howfar($height,$ratio+1,$airspeed)
print "Glide ratio ",$ratio+1,":1, $distance from $height taking $time\n"
$distance=&howfar($height,$ratio-1)
print "With a glide ratio of ",$ratio-1,":1 you can fly $distance from $height\n"
print "Direct print: ",join ",",&howfar(5000,55,60)," not bad for no engine!\n"
sub howfar {
my ($height,$ratio,$airspeed)=@_
my ($distance,$time) # how to 'my' multiple variables
$airspeed*=$cnv2 # convert knots to kmph
$height =int($height/$cnv1)
$distance=int($height*$ratio/1000) # output result in kilometres not metres
if ($airspeed > 0) {
$time =int($distance/($airspeed/60))
return ($distance,$time)
} else {
return $distance
}
}
Herewejusttestthe$airspeedtoensurewewon'tbedoinganydivisionsby0.Italsoaffectswhatwereturn.Thereisalsoanew
print statement,whichshowsthatyoudon'tneedtoassigntointermediatevariables,orevenpassvariablesasparameters.
Constants,evilthingsthattheyare,workjustaswell.Ialreadymentionedthis,butademonstrationdoesn'thurt.Unlessyouwork
foranelectricchairmanufacturer.
Theastutereader.....:)EverytimeIreadthatIwonderwhatI'vemissed.Usuallysomethingobscurewhichtheauthorknows
nobodywillevernotice,butlikestobelittlethereader.Noexceptionhere!Anyway,youmaybewonderingwhythiswouldnothave
sufficedinsteadoftheif statement:
4/10/12 Robert's Perl Tutorial
73/86 www.sthomas.net/roberts-perl-tutorial.htm
sub howfar {
my ($height,$ratio,$airspeed)=@_
my ($distance,$time) # how to 'my' multiple variables
$airspeed*=$cnv2 # convert knots to kmph
$height =int($height/$cnv1)
$distance=int($height*$ratio/1000) # output result in kilometres not metres
$time =int($distance/($airspeed/60)) if $airspeed > 0
return ($distance,$time)
}
Afterall,thefirstitemreturnedis$distance,sothereforeitshouldbethefirstoneassignedvia:
$distance=&howfar($height,$ratio-1)
and$timeshouldjustdisappearintothebitbucket.
Theanswerlieswithscalarsandlists.Wearereturningalist,butassigningittoascalar.Whathappenswhenyoudothat?The
scalartakesonthelastvalueofthelist.Thelastvalueofthelistbeingreturnedisofcourse$time,whichishasbeendeclared
butnototherwisetouched.Therefore,itisnothingandappearsassuchontheprintedstatement.Asmallprogramtodemonstrate
thatpoint:
$word=&wordfunc("Greetings")
print "The word is $word\n"
(@words)=&wordfunc("Bonjour")
print "The words are @words\n"
sub wordfunc {
my $word=shift # when in a subroutine, shifts @_ if no target specified
my @words # how to my an array
@words=split //,$word # splits on the nothings between each letter
($first,$last)=($words[0],$words[$#words]) # see section on Arrays if required
return ($first,$last) # Returns just the first and last
}
Asyoucansee,thefirstcallprintstheletter's',whichisthelastelementofthelistthatisreturned.Youcouldofcourseusealist
consistingofjustoneelement:
($word)=&wordfunc("Greetings")
Nowweareassigningalisttoalist,soperlstartsatthefirstelementandkeepsassigningtillitrunsoutofelements.Theparens
turnsalonelyscalarintoanelementofalist.Youmightconsideralwaysassigningtheresultsofsubroutinesthisway,asyounever
knowwhenthesubroutinemightchange.IknowI'vejustevangelisedabouthowsubroutinesshouldn'tchange,butifyoutakecare
andthesubroutinewritetakescare,theredefinitelywon'tbeanyproblems!
That'saboutitforgoodoldmy .Thereisalotmoretolearnaboutitbutthat'senoughtogetstarted.Younowknowaboutalittle
aboutvariablevisibility,andIdon'tmeanchangeableweather.
Local
ThereisonemorefunctionthatI'dliketodrawtoyourattention,andwe'lllaunchstraightintothedemonstration:
@words=@ARGV
print "Output Field Separator is :$,:\n"
print '1. Words:', @words, "\n"
&change
$,='_'
print "\nOutput Field Separator is :$,:\n"
print '2. Words:', @words, "\n"
&change
4/10/12 Robert's Perl Tutorial
74/86 www.sthomas.net/roberts-perl-tutorial.htm
sub change {
print ' Words:', @words, "\n"
}
whichshouldbeexecutedsomethinglikethis:
perl test.pl sarcasm is the lowest form of wit
Thespecialvariable$, defineswhatPerlshouldprintinbetweenlistsitisgiven.Bydefault,itisnothing.Sothefirsttwoprints
shouldhavenospacesbetweenthewords.Thenweassign'_'to$, sothenextprintshaveunderscoresbetweenthewords.
Ifwewanttouseadifferentvaluefor$, inthechange subroutine,andnotdisturbthemainvalue,wehavealittleproblem.This
problemcannotbesolvedbymy becauseglobalvariableslike$, cannotatthistimebelexicallyscoped.So,wecouldmanuallydo
it:
@words=@ARGV
print "Output Field Separator is :$,:\n"
print '1. Words:', @words, "\n"
&change
$,="_"
print "\nOutput Field Separator is :$,:\n"
print '2. Words:', @words, "\n"
&change
sub change {
$save=$,
$,='*'
print ' Words:', @words, "\n"
$,=$save
}
Thatworks,butitismessy.Perlhasaspecialfunctionforoccasionsofthisnature,calledlocal .Anexampleoflocal inaction:
@words=@ARGV
print "Output Field Separator is :$,:\n"
print '1. Words:', @words, "\n"
&change
$,="_"
print "\nOutput Field Separator is :$,:\n"
print '2. Words:', @words, "\n"
&change
sub change {
local $,="!-!"
print ' Words:', @words, "\n"
}
Youcantryitwithmy insteadbutitwon'twork.I'msureyou'lltryitanyway,Iknowyoulearnthingsthehardwayotherwiseyoua)
wouldn'tbeprogrammingcomputersandb)wouldn'tbeusingthistutorialtodoit.
Thelocal functionworksinasimilarwaytomy ,butassignstemporaryvaluestoglobalvariables.Themy functioncreatesnew
variablesthathavethesamename.Thedistinctionisimportant,butthereasonsrequireperlproficiencybeyondthescopeofthis
humbletutorial.Inpractice,thedifferenceis:
lexicallyscopedvariables(thosedeclaredwithmy )arefasterthannonlexicallyscopedvariables.
local variablesarevisibletocalledsubroutines.
my doesn'tworkonglobalvariableslike$, soyoumustuselocal .
4/10/12 Robert's Perl Tutorial
75/86 www.sthomas.net/roberts-perl-tutorial.htm
Returningarrays
Sothat'stheendofsubroutinesandparameters.WouldyoubelieveIhaveonlyscratchedthesurface?Thereareclosures,
prototypes,autoloadingandreferencestolearn.Not,however,inthistutorial.Atleastnotyet.I'llfinishwithonelastdemonstration.
YoumayhavenoticedthatPerlreturnsonelonglistfromsubroutines.Thisisfine,butsupposeyouwanttwoseparatelists,for
exampletwoarrays?Thisisonewaytodoit:
($w1,$w2)=&wordfunc("Hello World") # Assign the array references to scalars
print "@$w1 and @$w2\n" # deference, ie access, the arrays referred to
#print "$w1 and $w2\n" # uncomment this next time round
sub wordfunc {
my $phrase=shift
my (@words,@word1,@word2) # declare three variables lexically
@words=split /\s+/,$phrase # split the phrase on whitespace
@word1=split //,$words[0] # create array of letters from the first word
@word2=split //,$words[1] # and the second
return (\@word1,\@word2) # return references to the two arrays -- scalars
}
Thereisalotgoingonthere.Itshouldbeclearupuntilthereturn statement.Asweknow,Perlonlyreturnsasinglelist.So,we
makePerlreturnalistofthearraysithasjustcreated.Nottheactualarraysthemselves,butreferencestothearrays.Abitlikea
shoppinglistisajustabitofpaper,nottheactualgoodsitself.Thereferenceiscreatedbyuseofthe\ backslash.
Havingreturnedtwoarrayreferencestheyareassignedtoscalarvariables.Ifyouuncommentthesecondprintlineyou'llseetwo
referencestoarrays.
Thenextproblemishowtodereferencethereferences,oraccessthearrays.Theconstruct@$xxx doesthatforus.IknowIsaidI
wouldn'tcoverreferences,andIhaven'tthatisjustausefultrick.
Thislittlesectionisnotdesignedasacompleteguide,itisjustatasterofthingstocome.Perlisimmenselypowerful.Ifyouthink
somethingcan'tbedone,theproblemislikelytobeitisbeyondyourability,notthatofPerl.
Modules
Anintroduction
Subroutinesareoftusedpiecesofcode.Theyexistsoyoucanreusethecodeandnothavetoconstantlyrewriteit.
Amoduleis,inprinciple,similartoasubroutine.Itisalsoanoftusedpieceofcode.Thedifferenceisthatmodulesdon'tliveinyour
program,theyaretheirownseparatescriptoutsideyourcode.Forexample,youmightwritearoutinetosendemail.Youcould
thenusethiscodeinten,ahundred,athousanddifferentprogramsjustbyreferencingtheoriginalprogram.
Asyouwouldexpect,thebasicPerlpackageincludesalargenumberofmodules.Thesehavebeenwrittenbypeoplewhohada
needforthecode,madeitamoduleandreleaseditintothebigwideworld.Manyofthesemoduleshavebeendebugged,
improvedanddocumentedbyyetmorepeople.ToquotetheOpenSourcemantra,allbugsareshallowunderthescrutinyofevery
programmer.
AsidefromthemanymodulesincludedwithPerltherearehundredsmoreavailableonCPAN,theComprehensivePerlArchive
Network.Refertoyourdocumentationfordetails.
File::Findusingamodule
AnexampleofamoduleincludedwithPerlisFile::Find.ThereareseveralmodulesundertheFile::Findsection,suchas
File::Basetree,File::CompareandFile::Stat.
ThisisanexampleofhowFile::Findcanbeused:
4/10/12 Robert's Perl Tutorial
76/86 www.sthomas.net/roberts-perl-tutorial.htm
use File::Find
$dir1='/some/dir/with/lots/of/files'
$dir2='/another/directory/'
find(\&wanted, $dir1,$dir2)
sub wanted {
print "Found it $File::Find::dir/$_\n" if /^[a-d]/i
}
Thefirstlineisthemostimportant.Theuse functionloadstheFile::Findmodule.Now,allthepowerandfunctionalityof
File::Findisavailableforuse.Suchasthefind function.Thisacceptstwobasicparameters:
Thenameofasubroutine,usuallywantedwhichdefineswhatyouwanttodowiththelistoffilesbeingreturned.The
filenamewillbein$_.
Alistofdirectoriestobesearched.Subdirectorieswillalsobesearched.
Thesubroutinewantedsimplyprintsthedirectorythefilewasfoundinifthefilenamebeginswitha,b,cord.Makeyourownregex
tosuit.Theline$File::Find::dirmeansthe$dirvariableinthemodule$File::Find.Thisisexplainedfurtherinthenext
section.
Notethe\&wantedparameterisareferencetoasubroutine.Essentially,thismeansthatthecodeinFile::Findknowswhere
tofindthe&wantedsubroutine.ItisbasicallylikeshortcutsunderWindows9xandNT4,insteadofactualfiles(buttheUNIXPerl
peoplewouldslaughtermeforthat,sobequiet).
ChangeNotify
AnotherexampleisWin32::ChangeNotify.AsyoumightexpectthereareanumberofWin32specificmodules,and
ChangeNotifyisoneofthem.Itwaitsuntilasomethingchangesinadirectory,thenacts.Whatitwaitsforandwhatitdoesareupto
you,forexample:
use Win32::ChangeNotify
$Path='/downloads'
$WatchSubTree=0
$Events='FILE_NAME'
$browser='E:/progs/netscape/Communicator/program/netscape.exe'
$changes=0
$notify = Win32::ChangeNotify->new($Path,$WatchSubTree,$Events)
while (1) {
print "- ",scalar(localtime)," $changes so far to $Path.\n"
$notify->wait
++$changes
print "- ",scalar(localtime), " Launching $browser...\n"
system("$browser $Path")
$notify->reset
}
Again,themoduleisincorporatedintotheprogramwithuse .Anobjectreferredtobythevariable$notifyiscreated.The
parameterspassedarethepathtobewatched,whetherwewanttowatchsubtrees,andwhatsortofeventswewanttobenotified
about,inthiscaseonlyfilenamechanges.
Then,weenteraloopwhichcontinueswhile1istruewhichwillbeforever.
Theprogrampauseswhenthewaitmethodofthe$notifynotifyobjectiscalled.Onlywhenthereisachangetothedirectory,
thentherestofthesubroutinecompletes,launchingthebrowser.Wehavetoresetthe$notifyobject.
Thereissomeprettyfrighteningstuffaboutobjectsintheexplanation.Butyoudon'tactuallyneedtounderstandanythingabout
objects.Justreadthedocumentation,andexperiment.
Youcanuseasmanymodulesasyoulikeinoneprogram.Astheyareallwrittenwithcarefullyscopedvariablesyouneednot
4/10/12 Robert's Perl Tutorial
77/86 www.sthomas.net/roberts-perl-tutorial.htm
worryaboutprogrammersusingthesamevariablenamesindifferentmodules.Nowyou*really*appreciatescoping!
YourVeryOwnModule
Youtoocanwriteyourownmodules.Itiseasy.First,wewillcreatethefantasticbitofcodethatwewanttoreuseeverywhere.
First,we'llwriteanormalPerlprogram:
$name=shift
print &logname($name)
sub logname {
my $name=shift
my @namebits
my ($logon,$inital)
@namebits=split /\s+/,$name
($inital)=$name=~/(\w)/
$logon=$inital.$namebits[$#namebits]
$logon=lc $logon
return $logon
}
Executelikesoperl script.pl "Nick Bladon"
Thescriptitselfisnothingamazing.Thelc functionstandsforLowerCase,orprobablylOWERcASEyoucanseewhatitdoes.
Inordertoturnitintoamodulecarryoutthefollowingsteps:
1. FindoutwhereyourcopyofPerlisinstalled,forexamplec:\progs\perl.
2. Withinthatdirectorythereshouldbealibdirectory.
3. Makeadirectorywithinlib,forexamplec:\progs\perl\lib\RMP\
Nowwe'llmakethemodule.Remember,amoduleisjustcodeyouaregoingtoreuse.Sowedon'tneedalloftheaboveexample.
Justthisbit:
sub logname {
my $name=shift
my @namebits
my ($logon,$inital)
@namebits=split /\s+/,$name
($inital)=$name=~/(\w)/
$logon=$inital.$namebits[$#namebits]
$logon=lc $logon
return $logon
}
1
Thebitthathasbeenaddedisthe1atthebottom.Why?Perlrequiresthatallmodulesreturntrue.Weknowthatasubroutine
alwaysreturnsthevalueofthelastexpressionevaluated.As1evaluatestotrue,that'lldo.
Youneedtosavethisaslogon.pminyournewlycreateddirectoryunderlib.ThepmstandsforPerlModule.
That'sit.Amodulecreated.Touse,justmakeanormalPerlscriptsuchas:
use RMP::logon
$name=shift
print logname($name)
andheypresto!Modulepowerisyours!
Youdon'thavetocreateyourownsubdirectorywithinlibbutIwouldadviseitforthesakeofneatness.Andasyoumightexpect,
thereisalotmoretolearnaboutmodulesbutthisissupposedtobeabasictutorial,sothat'senoughforthetimebeing.
4/10/12 Robert's Perl Tutorial
78/86 www.sthomas.net/roberts-perl-tutorial.htm
BondageandDiscipline
Perlisaveryflexiblelanguage.Itisdesignedasahackingtool,forquicksysadminmagic.Itcandoquiteabitmorebesides,but
beingsmallandpowerfulisacorePerlfeature.EarlieronIsaidPerlisnotabondageanddisciplinelanguagetoqualifythat,it
doesn'thaetobe.However,thereisatimeandplaceforeverything.
Fortinyscriptsyoudon'twanttobedeclaringvariables,typecastingandgenerallyspendingmoretimeobeyingrulesthanyoudo
gettingthejobdone.So,Perldoesn'tforceyoutodoallofthesegoodprogrammingpractices.However,notallyourprogramsare
goingtobefiveminutehacks.Somewillbeprettylarge.Therefore,someDisciplineisinorder.
Perlhastwoprimarymethodsofenforcingdiscipline.Theyare:
-w forWarnings
use strict
w
Considerforamomentthislittleprogram:
@input=@ARGV
$outfile='outfile.txt'
open OUT, ">$outfile" or die "Can't open $outfile for write:$!\n"
$input2++
$delay=2 if $input[0] eq 'sleep'
sleep $delay
print "The first element of \@input is $input[0]\n"
print OUY "Slept $delay!\n"
Itdoesn'tdomuch.Justprintsoutthefirstargumentsupplied,anddemonstratestheuninspiringsleep function.Theprogram
itselfisfullofholes,anditisonlyafewlines.Howmanyerrorscanyouspot?Tryandcountthem.Whenyouarefinished,execute
theprogramwitherrorchecksenabled:
perl -w script.pl hello
Perlfindsquiteafewerrors.The-w switchfinds,amongotherheinoussins:
Variablesusedonlyonce.Intheexample,$input2isusedonlyonce.Itisauselessvariable.
Filehandlesusedincorrectly.Withprint OUYI'mtryingtoprinttoanonexistentfilehandle.With-w analarmisraised,asit
wouldbeifItriedtowritetoafilehandlewhichwasreadonly.
Useofuninitialisedvariables.Thevariable$delayisuninitialisedif'sleep'isnotthefirstparameter.Makingvariablesspring
intotheairondemandisnotgoodprogrammingpractice.Theyshouldbedefinedcarefullyfirst.
So,generally,-w isaGoodThing.Itforcesyoutowritecleanercode.Souseit,butdon'tbeafraidnottoforveryshortprograms.
Shebang
Youknowthatyoucanturnwarningsonwith-w onthecommandline.Youcanalsoturnthemonwithinthescriptitself.Forthat
matter,youcangiveperlanycommandlineoptionwithinthescriptitself.Forexample:
perl script.pl hello
toexecutethis:
#!perl -w
@input=@ARGV
$outfile='outfile.txt'
open OUT, ">$outfile" or die "Can't open $outfile for write:$!\n"
4/10/12 Robert's Perl Tutorial
79/86 www.sthomas.net/roberts-perl-tutorial.htm
$input2++
$delay=2 if $input[0] eq 'sleep'
sleep $delay
print "The first element of \@input is $input[0]\n"
print OUY "Slept $delay!\n"
hasthesameeffectas:
perl -w script.pl hello
Itmaybemoreconvenientforyoutoputtheflaginsidethescript.Itdoesn'thavetobejust-w ,itcanbeanyargumentPerl
supports.Run
perl -h
forafulllist.
Thefirstline,#!perl -wistheshebangline.ThisisderivedfromUNIX,wherePerlwasfirstdeveloped.UNIXsystemsmakea
scriptexecutablebychanginganattribute.Theoperatingsystemthenloadsthefileandworksouthowtoexecuteitinthiscase
bylookingatthefirstline,thenloadingtheperlinterpreter.Windowssystemsknowthatallfileswithacertainextensionmustbe
passedtoacertainprogramforexecution,egall.batfilesarepassedtocommand.com,andall.xlsfilesarepassedtoExcel.The
pointofallthisbeingthatyoudon'tneedashebangline,butitdoesn'thurt.
usestrict
Sowhat'sstrictandhowdoyouuseit?Themodulestrict restricts'unsafeconstructs',accordingtotheperldocs.Thestrict
moduleisapragma,whichisahintthatmustbeobeyed.Likewhenyourgirlfriendsays'oh,thatringis*far*tooexpensive'.
Thereisnoneedtobefrightenedaboutunsafecodeifyoudon'tmindendlesshoursofdebuggingunstructuredprograms.When
youenablethestrict module,thethreethingsthatPerlbecomesstrictaboutare:
Variables'vars'
References'refs'
Subroutines'subs'
Thistutorialdoesn'tpresentlycoverreferences(andlet'shopeIremembertoremovethissentenceifIdocoveritinlaterversions)
sowewon'tworryaboutrefs.
Strictvariablesareuseful.Essentially,thismeansthatallvariablesmustbedeclared,thatisdefinedbeforeuseratherthan
springingintoexistenceasrequired.Furthermore,eachvariablemustbedefinedwithmy orfullyqualified.Thisisanexampleofa
programthatisnotstrict,andshouldbeexecutedsomethinglikethis:
perl script.pl "Alain James Smith"
wherethe""enclosethestringasasingleparameterasopposedtothreeseparatespacedelimitedparameters.
#use strict # uncomment after running a couple of times
$name=shift # shifts @ARGV if no arguments supplied
print "The name is $name\n"
$inis=&initials($name)
$luck=int(rand(10)) if $inis=~/^(?:[a-d]|[n-p]|[x-z])/i
print "The initials are $inis, lucky number: $luck\n"
sub initials {
my $name=shift
$initials.=$1 while $name=~/(\w)\w+\s?/g
return $initials
}
Bynowyoushouldbeabletoworkoutwhattheabovedoes.Whenyouuncommenttheuse strict pragma,andrerunthe
program,youwillgetoutputsomethinglikethis:
4/10/12 Robert's Perl Tutorial
80/86 www.sthomas.net/roberts-perl-tutorial.htm
Global symbol "$name" requires explicit package name at n1.pl line 3.
Global symbol "$inis" requires explicit package name at n1.pl line 6.
Global symbol "$luck" requires explicit package name at n1.pl line 8.
Global symbol "$initials" requires explicit package name at n1.pl line 14.
Execution of n1.pl aborted due to compilation errors.
ThesewarningsmeanPerlisnotexactlyclearaboutwhatthescopeofvariablesis.IfPerlisnotclear,youmightnotbeeither.So
youneedtobeexplicitaboutyourvariables,whichmeanseitherdeclaringthemwithmy sotheyarerestrictedtothecurrentblock,
orreferringtothemwiththeirfullyqualifiedname.Anexample,usingbothmethods:
use strict
$MAIN::name=shift # shifts @ARGV if no arguments supplied
print "The name is ",$MAIN::name,"\n"
my $inis=''
my $luck=''
$inis=&initials($MAIN::name)
$luck=int(rand(10)) if $inis=~/^(?:[a-d]|[n-p]|[x-z])/i
print "The initials are $inis, lucky number: $luck\n"
sub initials {
my $name=shift
my $initials
$initials.=$1 while $name=~/(\w)\w+\s?/g
return $initials
}
Themy variablesinthesubroutinearenothingnew.Themy variablesoutsidethesubroutineare.Ifyouthinkaboutit,themain
programitselfisalsoakindofblock,andthereforevariablescanbelexicallyscopedtobevisibleonlywithintheblock.
Theotherinterestingbitisthe$MAIN::namebusiness.This,asyoumightexpect,isthefullyqualifiednameofthevariable.The
firstpartisthepackagename,inthiscaseMAIN.Thesecondpartistheactualvariablename.Personally,I'veneverneededto
refertoavariablethisway.I'mnotsayingyou'llneverusethesyntax,butIwouldsuggestthatknowingthisisnotonaperlstudents
Top10listofThingstoMaster.
Theimportantthingaboutuse strict isthatitdoesenforcemoredisciplinethanyouhavebeenusedto,andforallbutthe
smallestofprograms,thatismostdefinitelyaGoodThing.
Debugging
Soonerorlateryou'llneedtodosomefairlyhairydebugging.Itwillbelaterifyouareusingstrict ,-w andwritingyour
subroutinesproperly,butthemomentwillcome.
Whenitdoesyou'llbeporingovercode,probablylateatnight,wonderingwherethehelltheproblemis.SometechniquesIfind
usefulare:
Printyourvariablesandotherinformationoutatfrequentintervals.
Splitdifficultcomponentsoftheprogramoutintosmall,throwawayscripts.Gettheseworking,thencopytheresultsbackinto
themainprogram.
#Commentfrequently.
Eventually,you'llbestuck.Suchisthepriceofprogress.Inthiscase,Perl'sowndebuggercanbeinvaluable.Runthiscodeas
normalfirst:
$name=shift
print "Logon name creation program\n:Converting '$name'\n"
print &logname($name),"\n\n"
print "Program ended at", scalar(localtime),"\n"
4/10/12 Robert's Perl Tutorial
81/86 www.sthomas.net/roberts-perl-tutorial.htm
sub logname {
my $name=shift
my @namebits
my ($logon,$inital)
@namebits=split /\s+/,$name
($inital)=$name=~/(\w)/
$logon=$inital.$namebits[$#namebits]
$logon=lc $logon
return $logon
}
We'llrunitwiththedebuggersoyoucanwatchperlatworkwhileitruns:
perl -d script.pl "Peter Dakin"
andyouareintothedebugger,whichshouldlooksomethinglikethis:
c:\scripts\db.pl>perl -d db.pl "Peter Dakin"
Loading DB routines from perl5db.pl version 1.0401
Emacs support available.
Enter h or `h h' for help.
main::(db.pl:1): $name=shift
DB<1>
db.pl Nameofscriptbeingexecuted
1 Linenumberofscriptthatisjustabouttobeexecuted.
$name=shift Thecodethatisjustabouttobeexecuted.
Typesforasinglestepandpressenter.Thecode$name=shiftwillbeexecuted,andperlwaitsforyournextcommand.Keep
inputtingsuntiltheprogramterminates.
Thisbyitselfisusefulasyouseethesubroutineflow,butifyouenterhforhelpyou'llseeabewilderingrangeofdebugoptions.I
won'tdetailthemallhere,butsomeoftheonesIfindmostusefulare:
n
Executesmainprogram,butskipssubroutinecalls.Thesubroutineisexecuted,butyouaren'tsteppedthroughit.Try
usingn insteadofs .
/xx/ Searchesthroughprogramforxx
p Prints,forexamplep @namebits,p $name
Enter PressingtheEnterkey(inputtingacarriagereturn)repeatsthelastn ors command.
perlcode
Youcantypeanyperlcodeinanditwillbeevaluated,andhaveaeffectonyourprogram.IntheexamplebelowI
removespacesfrom$name.Inputsinbold:
main::(db.pl:1): $name=shift
DB<1> s
main::(db.pl:3): print "Logon name creation program\n:Converting '$name'\n"
DB<1> $name=s/\s//g
DB<2> print$name
MarkGray
DB<3>
Therearemany,manymoredebuggeroptionswhichareworthbecomingfamiliarwith.Typeh forafulllist.
LogicalOperators
LogicaloperatorsaresuchthingsasOR,NOT,AND.Theyallevaluateexpressions.Theexpressionevaluatestotrue,orfalse.
Exactlywhatcriteriaforevaluationareuseddependsontheoperator.
4/10/12 Robert's Perl Tutorial
82/86 www.sthomas.net/roberts-perl-tutorial.htm
or
Theor operatorworksasfollows:
open STUFF, $stuff or die "Cannot open $stuff for read :$!"
ThislinemeansiftheoperationforopeningSTUFFfails,thendosomethingelse.Anotherexample:
$_=shift
/^R/ or print "Doesn't start with R\n"
Iftheregularexpressionisfalse,thenwhateverisontheleftsideoftheor isprinted.Asyouknow,shift workson@ARGV ifno
targetisgiven,or@_ insideasubroutine.
PerlhastwoOR operators.Oneisthenowfamiliaror andtheotheris|| .
Precedence:WhatcomesFirst
Tounderstandthedifferencebetweenthetwoweneedtotalkaboutprecedence.Precedencemeanspriority,order,importance.A
goodexampleis:
perl -e"print 2+8
whichweknowequals10.Butifweadd:
perl -e"print 2+8/2
Now,willthisbe2+8==10,dividedby2==5?Ormaybe8/2==4,plus2==6?
Precedenceisaboutwhatisdonefirst.Intheexampleabove,youcanseethatthedivisionisdonefirst,thentheaddition.
Therefore,divisionhasahigherprecedencethataddition.
Youcanforcetheissuewithparens:
perl -e"print ((2+8)/2)
whichforcesPerl,kickingandscreaming,toevaluate2+8thendividetheresultby2.
Sowhathasthistodowithlogicaloperators?Well,themaindifferencebetweenor and|| isprecedence.
Intheexamplebelow,weattempttoassigntwovariablestononexistentelementsofanarray.Thiswillfail:
@list=qw(a b c)
$name1 = $list[4] or "1-Unknown"
$name2 = $list[4] || "2-Unknown"
print "Name1 is $name1, Name2 is $name2\n"
print "Name1 exists\n" if defined $name1
print "Name2 exists\n" if defined $name2
Theoutputisinteresting.Thevariable$name2hasbeencreated,albeitwithafalsevalue.However,$name1doesnotexist.The
reasonisallaboutprecedence.Theor operatorhasalowerprecedencethan|| .
Thismeansor looksattheentireexpressiononitslefthandside.Inthiscase,thatis$name1 = $list[4] .Ifitistrue,itgets
done.Ifitisfalse,itisnotandtherighthandsideisevaluated,andthelefthandsideisignoredasifitneverexisted.Inthe
exampleabove,oncetheleftsideisfoundtobefalse,thenalltherightsideevaluatesis"1-Unknown"whichmaybetruebut
doesn'tproduceanyoutput.
Inthecaseof|| ,whichhasahigherprecedence,thecodeimmediatelyontheleftoftheoperatorisevaluated.Inthiscase,that
is$list[4].Thisisfalse,sothecodeimmediatelytotherightisevaluated.But,theoriginalcodeontheleftwhichwasnot
evaluated,$name2 = isnotforgotten.Therefore,theexpressionevaluatedto$name2 = "2-Unknown".
4/10/12 Robert's Perl Tutorial
83/86 www.sthomas.net/roberts-perl-tutorial.htm
Theexamplebelowshouldhelpclarifythings:
@list=qw(a b c)
$ele1 = $list[4] or print "1 Failed\n"
$ele2 = $list[4] || print "2 Failed\n"
print <<PRT
ele1 :$ele1:
ele2 :$ele2:
PRT
Thetwofailurecodesarebothprinted,butfordifferentreasons.Thefirstisprintedbecauseweareassigning$ele1afalsevalue,
sotheresultoftheoperationisfalse.Therefore,therighthandsideisevaluated.
Thesecondisprintedbecause$list[4]itselffalse.Yet,asyoucansee,$ele2exists.Anyideawhy?
Thereasonisthattheresultofprint "2-Failed\n"hasbeenassignedto$ele2.Thisissuccessful,andthereforereturns1.
Anotherexample:
$file='not-there.txt'
open FILE, $file || print "1: Can't open file:$!\n"
open FILE, $file or print "2: Can't open file:$!\n"
Inthefirstexample,theerrormessageisnotprinted.Thisisbecause$fileisevaluatingtotrue.However,inthesecondexample,
or looksattheentireexpression,notjustwhatisimmediatelytotheleftandtakesactionontheresultofevaluatingtheentireleft
handside,notjusttheexpressionimmediatelytoitsleft.
Youcanfixthingswithparens:
$file='not-there.txt'
open FILE, $file || print "1: Can't open file:$!\n"
open FILE, $file or print "2: Can't open file:$!\n"
open (FILE, $file) || print "3: Can't open file:$!\n"
likeso,butwhybotherwhenyouhaveaperfectlygoodoperatorinor ?Youcouldapplyparenselsewhere:
@list=qw(a b c)
$name1 = $list[4] or "1-Unknown"
($name2 = $list[4]) || "2-Unknown"
print "Name1 is $name1, Name2 is $name2\n"
print "Name1 exists\n" if defined $name1
print "Name2 exists\n" if defined $name2
Now,($name2 = $list[4]) isevaluatedasacompleteexpression,notjustas$list[4]isevaluatedasacompleteexpression,
notjustas$list[4],sowegetexactlythesameresultasifweusedor .
And
nowforsomethingsimilar.And.LogicalANDoperatorsevaluatetwoexpressions,andreturntrueonlyifbotharetrue.Contrast
thiswithOR,whichreturnstrueonlyofoneormoreofthetwoexpressionsaretrue.PerlhasafewANDoperators.
ThefirsttypeofANDwewilllookatis&& :
4/10/12 Robert's Perl Tutorial
84/86 www.sthomas.net/roberts-perl-tutorial.htm
@list=qw(a b c)
print "List is:@list\n"
if ($list[0] eq 'x' && $list[2]++ eq 'd') {
print "True\n"
} else {
print "False\n"
}
print "List is:@list\n"
Theoutputhereisfalse.Itisclearthat$list[0]doesnotequalx .AsANDstatementscanonlyreturntrueifbothexpressions
beingevaluatedaretrue,thenasthefirststatementisfalsethisisanobviousnonstarterandperldecidesitneednotcontinueto
thesecondstatement.Entirelysensible.
ThesecondtypeofANDstatementis& .Thisissimilarto&& .Seeifyoucanworkoutwhatthedifferenceisusingthisexample:
@list=qw(a b c)
print "List is:@list\n"
if ($list[0] eq 'x' & $list[2]++ eq 'd') {
print "True\n"
} else {
print "False\n"
}
print "List is:@list\n"
Thedifferenceisthatthesecondpartoftheexpressionisevaluatednomatterwhattheresultofthefirstpartis.Despitethefact
thattheANDstatementcannotpossiblyreturntrue,perlgoesaheadandevaluatesthesecondpartofthestatementanyway,
hence$list[2]endsupasd .
ThethirdANDwhichwewilllookatisand .Thisbehavesinthesamewayas&& butislowerprecedence.Therefore,allthe
guidelinesabout|| andor apply.
OtherLogicalOperators
Perlhasnot ,whichworkslike! exceptforlowprecedence.Ifyouarewonderingwhereyouhaveseen! before,whatabout:
$x !~/match/
if ($t != 5) {
astwoexamples.ThereisalsoExclusiveOR,orXOR.Thismeans:
Ifoneexpressionistrue,XORreturnstrue
Ifbothexpressionsarefalse,XORreturnsfalse
Ifbothexpressionsaretrue,XORreturnsfalse(thecrucialdifferencefromOR)
Thisneedsanexample.JaneandSoniaaretwoknowntroublemakers,withareputationforthrowinggoodbeeraround,going
toplessatinappropriatemomentsandsingingoutoftunetothekaraokemachine.Youonlywanttoletoneofthemintoyourparty,
andinsteadofabigmuscleboundbounceryouhavethisperlscriptonthedoor:
($name1,$name2)=@ARGV
if ($name1 eq 'Jane' xor $name2 eq 'Sonia') {
print "OK, allowed\n"
} else {
print "Sorry, not allowed\n"
}
Iwouldsuggestrunningitthus:
perl script.pl Jane Karen (one true, one false)
perl script.pl Jim Sonia (one true, one false)
4/10/12 Robert's Perl Tutorial
85/86 www.sthomas.net/roberts-perl-tutorial.htm
perl script.pl Jane Sonia (both true)
perl script.pl Jim Sam (both false)

Well,thescriptisnotperfectasadoorman,asallJaneandSoniahavetodoistypetheirnamesinlowercase,buthopefullyit
demonstratedxor .
Onethingtobewareofis:
$_=shift
print "OK\n" unless not(!/r/i || /o/i & /p/ or /q/)
overcomplication,andbelievemetheaboveisnotascomplicatedasitcouldbe.Takethetimetounderstandwhatyouwanttodo.
Perlprovidesaplethoraoflogicaloperandssoyoureallydon'thaveanyexcusefornotwritinglegiblecode.Theabovecanbe
writtenalotmoreconciselyandclearly.Aswellasalotmoreobscurely:)
@ARGV
Lastwords
Ihopeyouhaveenjoyedthistutorialandlearntsomethingfromit.Iwouldappreciateanemaillettingmeknowhowitcouldbe
improved.WhatyouhavelearntisjustafractionofPerl'sfunctionality,butyou'llfindskillslikeregexescanbeappliedinmanyother
placesthanPerl.
Goodluck.
--
Robert
Thanksto...
Everyonethathelpedinthedevelopmentofthistutorial.Idoreadallthefeedbackemails,butdon'talwaysactionthemthesame
year.Whatyouhavejustreadisbetterbecauseofthepeoplebelow.Theyfixthebugs,screamwhentheydon'tunderstandandI
rewritewholesections.Documentslikethisarewrittenbytheauthors,butpolishedbythereaders.
Therollofhonouris,inasemichronologicalorder:
MarkMillerforhislongemailsuggestingimprovementsandhighlightingtypos.IcringedwhenIrealisedwhatI'dletthrough
:(
RolandtowhomIameternallygratefulforsendinginmanytyporeports,andpointingoutwherehedidn'tunderstandan
explanation.
KatyadeVriesforfindingHTMLerrorsandproblemswiththeexamplecode.
StevenHamforbeingpickyaboutspellingerrors.Goodgoing,consideringEnglishishissecondlanguage!
CarlosJaramilloUribeforpointingoutwhereIcouldhaveexplainedpostincrementsandregexalittlebetterandforpointing
outatypoortwo.
SergioPoliniwhobroughtaninterestingaspectPerl'sbehaviourwitharraystomyattention,andhelpingtoimprovepartsof
theRegexsection.
LeoDurocherfortellingmehehadtroublewiththeregexsection.Ifhedid,I'msuremanyothersdidtoo.
PaulTraffordforsolvingtheThem/UsproblemIwastoolazytobotherwith,anddoingitsoelegantly.
EricSmithwhowasoneofmanypeoplewhomademeatableofcontentsratherthanjusttellmeIshouldincludeone.I
neverusedanyofthem,andtheoneyouseenowisautogeneratedbyaprogramwritteninJava(onlykidding,itsnotauto
generated:)
MikeConkinwhosaidhedidn'tunderstand$^I.Goodpoint.I'dforgottentoexplainitatall.Mikewenttolistseveralother
areasIcoulddowithimprovinginoneofthemostamusingandusefulmissivesI'vehadonthetutorial.Thanks.
VasileCalamutiwhopickeduponmyuseofjoinbeforeI'dexplainedit,andacouplemoreoversights.
DidierOwonoforpointingoutmyoriginalexplanationof/ee didn'tmakesense.Hopefullythesecondversiondoes.
KeenMengLewandEverOlanowho,independently(Iassume)pickedupexactlythesametwotypos.Whicharenow
fixed.
AnnainOhiowhosentapoliteemailwithafewerrorsshepickedupon.
KenTeuchlerforknowingthedifferencebetween= and=~,andforhislonglistofimprovementswhichvariedfrom
grammarerrorstostylesuggestionstooversights.Ahugehelp.
cookie,firstlyforhisWin9xexperimentsanderrorchecksaboutmyexplanationofscoping.Secondlyforhismany
subsequentemailspointingoutminorproblemswhichelevatedhimtostatusof#1bugfixer.Appreciated.
GinnyforspottinganerrantwhichinthebesttraditionofteachersIhavechangedintoanexercisefordebugging,ofcourse
4/10/12 Robert's Perl Tutorial
86/86 www.sthomas.net/roberts-perl-tutorial.htm
Imeanttoleaveitoutinthefirstplace.Ishouldalsopointoutthatamajormotivationformedoputtheeffortintothistutorial
istheappreciationoftheuserbase,andGinnysentmeaparticularlymotivationalmissive.
JefferyJacksonfornoticingmyerrorabout0basedarrays.
KevinHaskinsforpointingoutNotepad'slimitationsandanequalityissue.
PatMcCarthyforpickingupasmalltypo.
BobKautenwhonoticedthatIhadn'texplainedtherangeoperatorproperly.Iblame....well,mereally.
AyhanTuncerforpickingupamistakewhereI'dcarelesslycutandpastedpastedpasted.ThenextdayMichaelKersey
foundtheexactsameerror,beforeI'dhadachancetofixit.Ayhanalsofoundquiteafewmoreerrorsafterthatoneduring
herworkontheTurkishtranslation.
RayPricewhowasanotheronewhofoundtheaboveerror,andacouplemoretyposaswell.
HenryVermeulen,aDutchchapwhonoticedI'dmispelledHeineken.NothingtodowithPerl,justoneofmyoutlandish
examples.
Everyonethathaseverworkedonperl,allthehackersontheperlwin32*mailinglists,ActiveStateandthenetizensof
clpm.
Theoriginallocationofthisdocumentis:
http://www.netcat.co.uk/rob/perl/win32perltut.html
Thistutorialiscopyright1997,1998,1999byRobertPepper.Reproductioninwholeorpartisprohibited.Pleasecontact
meifyouwanttousethisinformationanywhere.Thankyou.

RobertPeppermailto:[email protected]
Getyourownvisitormap!
QtProfessionalServicesQtConsulting,TrainingandAddonsDevelopmentMigrationMentoringwww.kdab.com
DataAcquisitionSystemAnalogDigitalI/O&FrameGrabberMotionControl&Machinevisionwww.daqsystem.com
QtExpertsAvailableC++andQtexperthelpforhire.Analysis,designorimplementationwww.fioniasoftware.dk

You might also like