Dette er del 4 av Kom igang med Powershell guiden min.
Del 1: Kom igang med Powershell
Del 2: Kom igang med Powershell
Del 3: Kom igang med Powershell
Frem til nå har vi sett på hvordan kommandolinjen fungerer i PowerShell . Vi skal nå se på nyttige mekanismer for å kunne skrive script i Powershell. I denne sammenheng skal vi blant annet ta en titt på variabler, objekter, operatører, løkker og valg.
Script er et sett med kommandoer satt sammen for å gjøre en jobb. Ofte samler vi operasjoner vi ønsker å gjenta i script, for slik å slippe å skrive inn alt på nytt hver gang vi ønsker å utføre jobben. For å jobbe med script trenger man i utgangspunktet ikke mer enn en editor, slik som Notepad. Jobber man mye med script kan det være man ønsker å investere i noe som har litt mer funksjoner, blant annet Intellisense (som foreslår fullførelse av det du skriver). Det finnes flere editorer du kan benytte, slik som Sapien Primalscript , PowerGUI Editor og Powershell ISE, bare bruk det som funker for deg. Jobber man mye med script bør man også tenke på hvordan man skal håndtere ulike versjoner av script. Til dette kan man benytte alt fra Sharepoint document librarys , CVS, TeamServer og Sourcesafe. En del av editorene har muligheter til å direkte jobbe mot disse kildene.
En helt enkel malfil kan se slik ut :
#=====================================================================
#
# NAME: Scriptets navn
#
# AUTHOR: Ragnar Harper
# CREATED DATE : 02.05.2007
#
# DESCRIPTION: Beskriv formål med scriptet
#
# HISTORY: Endringer noteres her
#
#=====================================================================
Etter Name: legger jeg scriptets navn. I tillegg legger jeg inn en beskrivelse av scriptet under Description , og en oppdateringslogg under History.
Selv om man husker hvorfor man lager scriptet når man utvikler det, kan det ofte være vanskeligere å huske det etter ett år..
Variabler identifiseres i PowerShell med $. Det betyr at alle variabler begynner med ett dollartegn. Vi skal i dette avsnittet se litt på bruken av variabler.
Benytt din favoritt editor til å skrive inn følgende linjer, som lagres som Script1.ps1:
$fornavn = Read-Host"Tast inn Fornavn"
$etternavn = Read-Host "Tast inn Etternavn"
Write-Host "Du heter $fornavn $etternavn"
I dette eksemplet oppretter vi to variabler, en navngitt $fornavn og en navngitt $etternavn. Vi benytter kommandoen Read-Host til å lese inn verdier i variabelene. Inntastingen av verdi avsluttes med at brukeren trykker <ENTER>.
Åpne PowerShell (hvis du ikke allerede har det åpent) og gå til mappen hvor du lagret script1.ps1
Start scriptet med å kjøre .\Script1.ps1
Du får da en dialog som ser noenlunde ut som denne:
Legg merke til at Read-Host autmatisk legger til : (kolon) bak teksten vår.
La oss gå videre, og lage scriptet Script2.ps1 :
$tallA = Read-Host("Tast inn tall 1")
$tallB = Read-Host("Tast inn tall 2")
Write-Host "Sum=$tallA+$tallB"
Lagre, og kjør scriptet. Hvordan ble resultatet denne gangen? Ble det som forventet?
La oss prøve oss på en endring av kildekoden. Endre linjen med Write-Host til å lyde som følger:
$tallA = Read-Host("Tast inn tall 1")
$tallB = Read-Host("Tast inn tall 2")
Write-Host (“Sum=” + ($tallA + $tallB))
Lagre Script2.ps1, og prøv å kjøre det en gang til. Ble resultatet annerledes nå?
Hos meg ble det slik:
Og dette skyldes at tallene håndteres som en tekststreng, og ikke som tall. Så hvordan kan vi fortelle PowerShell at vi jobber med tall?
I PowerShell kan vi eksplisitt angi hvilken datatype vi jobber med. Vi har frem til nå ikke hatt behov for det, og det er fordi PowerShell er veldig dyktig til å avgjøre hvilken datatype du ønsker å jobbe med. Men i enkelte tilfeller kan det være nødvendig å angi hva vi ønsker å ha. I Tabellen under ser du ulike datatyper du kan angi, men først et eksempel:
[int]$Tall1=Read-Host(“Tast inn tall 1”)
[int]$Tall2=Read-Host(“Tast inn tall 2”)
$tall1+$tall2
Når du kjører dette eksemplet, prøv også å lese inn bokstaver eller setninger. Som du ser feiler PowerShell da, med beskjed om at den ikke kan lagre verdien i en Integer. Du må altså oppgi et heltall for å få Powershell til å godta verdien.
La oss se på et annet eksempel også:
$x=”2”
$y=2
$x+$y
$y+$x
Hva skjer her? Jo, eksemplet viser at du kan sette de verdiene du vil til en variabel, og når du knytter dem sammen, slik som gjennom $x+$y og $y+$x så benytter Powershell datatypen til variabelen lengst til venstre for å avgjøre datatypen. I eksemplet $x+$y er $x en tekststreng (string), og svaret blir derfor to tekststrenger som blir satt sammen. I eksemplet $y+$x er $y et heltall, og Powershell prøver derfor automatisk å konvertere $x til heltall etter beste evne. Ettersom at tekststrengen ”2” kan oversettes til heltallet 2, utfører den operasjonen, men denne gangen altså med heltall verdier.
Tabell 2 Eksempel på typer og deres alias i PowerShell
|
PowerShell alias
|
Tilsvarende .NET type
|
|
[int]
|
System.Int32
|
|
[long]
|
System.Int64
|
|
[string]
|
System.String
|
|
[char]
|
System.Char
|
|
[bool]
|
System.Boolean
|
|
[byte]
|
System.Byte
|
|
[double]
|
System.Double
|
|
[decimal]
|
System.Decimal
|
|
[float]
|
System.Single
|
|
[single]
|
System.Single
|
|
[regex]
|
System.Text.RegularExpressions.Regex
|
|
[array]
|
System.Array
|
|
[xml]
|
System.Xml.XmlDocument
|
|
[scriptblock]
|
System.Management.Automation.ScriptBlock
|
|
[switch]
|
System.Management.Automation.SwitchParameter
|
|
[hashtable]
|
System.Collections.HashTable
|
|
[psobject]
|
System.Management.Automation.PSObject
|
|
[type]
|
System.Type
|
På mange av disse datatypene finnes datatypen som et array ( en samling med verdier ). Dette kan vi angi med [] etter datatypenavnet. Eksempel for int og long : [int[]] [long[]]
La oss si at vi ønsker å initisere variabelen $x som en samling med tallene 1.0, 2.2, 3.3 ,4.4, 5.5. Vi ønsker å lagre tallene som datatypen [double], en datatype som lar oss lagre desimaltall.
$x=1.0,2.2,3.3,4.4,5.5
$x.GetType()
Resultatet av denne operasjonen vil være et array – men av typen Object. Dette får du sjekket med komjmandoen $x.GetType(), som gir følgende utdata:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Hvorfor er dette et problem tenker du kanskje nå? Object funker bra i mange tilfeller, men kan gi feil senere. La oss se på følgende eksempel:
Resultatet ble riktig. Dette viser hvor kraftfullt PowerShell er til å ville gjøre det riktig for oss. Men, når datasamlingen vår er av type Object, betyr det at vi kan få den til å feile ganske enkelt, ved at de ulike verdiene kan være av forskjellige datatyper.
Merk! For sikkerhetsskyld bør du nullstille $sum variabelen på følgende måte:
$sum=””
Slik tester du det:
Denne gangen blir resultatet annerledes, og vi får ingen feilmelding. PowerShell bruker den første verdien den får inn i en variabel til å bestemme datatypen på denne variabelen. Det betyr at $sum denne gangen ble en [string] variabel, og at vi satte sammen tallene og bokstavene som om de var en tekst som skulle henge sammen.
For å sikre oss mot slike feil kan vi angi datatype og om det skal være en datasamling når vi tar i bruk variabelen.
Prøv følgende:
Denne gangen får du feilmelding når du prøver å sette verdien til ”R” fordi ”R” ikke er en verdi av type [double]
Du kan slette en variabel med Remove-Variable CmdLet’en. Verdier kan også slettes med Clear-Variable.
Det finnes også noen spesielle innebygde variabler. Jeg har satt disse opp i tabellen under. Det er viktig å kjenne til disse slik at du ikke benytter dem som egne variabel navn, og slik at du kan benytte dem.
Tabell 3 - Innebygde variabler
|
Variabelnavn
|
Beskrivelse
|
|
$_
|
Gjeldende objekt i pipeline. Dette benyttes hvor vi har behov for tilgang til hvert enkelt objekt som finnes i et resultat, enten for å sette verdier, lese verdier eller sammenligne verdier.
|
|
$^
|
Første tegnene (før mellomrom) i den siste linja som ble skrevet i shellet
|
|
$$
|
Siste tegnene (etter mellomrom) i den siste linja som ble skrevet i shellet
|
|
$?
|
Inneholder status på siste setning (suksess eller feil)
|
|
$args
|
Benyttes i funksjoner for å hente ut parametrene
|
|
$error
|
Hvis det oppstår feil lagres denne i $error
|
|
$home
|
Brukerens hjemmestasjon
|
|
$input
|
Inndata som pipes til en funksjon eller kodeblokk
|
|
$match
|
En tabell md elementene som er funnet i en –match operasjon
|
|
$ofs
|
Output field separator – benyttes når et array konverteres til en string. Standardverdien er mellomrom (space)
|
|
$shellid
|
Identifikasjonen til shellet. Benyttes til å finne ut executionpolicy og hvilke profiler som skal lastes
|
|
$stacktrace
|
Detaljert informasjon fra stack for siste feilmelding
|
Enkelt forklart kan vi si at et array er en container for å lagre ting. For de fleste behov trenger vi kun enkle array. For administrativ skripting kan du tenke på at array er en datasamling, altså en variabel som er istand til å inneholde flere verdier. I faglitteraturen møter du også begrepet collection. For dette kursets del kan du da tenke at det er et array. Dere får ha meg unnskyldt for at jeg i teksten benytter begrepet datasamling / array om hverandre. Da de fleste administratorer har liten erfaring med skripting og programmering, prøver jeg å gjøre det så forståelig som mulig.
La oss lage et enkelt array:
Vi kan aksessere hver enkelt verdi i et array gjennom å angi indeksen til verdien vi ønsker. I eksempelet over med $arr har vi 10 verdier. Disse aksesseres gjennom en indeks som starter på 0, og teller opp til så mange elementer som vi har. I dette tilfellet aksesseres altså hver verdi ved å angi indeks fra 0 til 9.
Den siste gir deg ingen verdi, da det kun ligger verdier fra 0 til 9.
Forøvrig kunne vi skrevet initiseringen av $arr på en annen, og mer effektiv måte:
La oss se på et mer praktisk rettet eksempel hvor vi ønsker å hente ut informasjon fra flere maskiner. Maskinene vi skal hente ut informasjon om ligger i fila server.txt. Server.txt fila er formatert med et servernavn på hver linje, slik som dette:
ServerA
ServerB
ServerC
Tips
! Lag din egen liste over servere i ditt eget nettverk.
La oss lese inn innholdet av fila til en variabel med navn $serverliste:
Jeg har benyttet en punktum til å angi maskinen jeg kjører på, de andre maskinene har jeg angitt med navn,
La oss gå igjennom hver server (hver linje, eller hvert element i array’et om du vil)
Om en av serverne ikke er tilgjenglig, eller begrenset med brannmur får du følgende melding:
Vi ser at serveren hyper1.harper.labs ikke var tilgjengelig under kjøring av koden.
Vi har allerede under kurset benyttet arrays og vi kommer til å benytte det flere ganger.
En annen type array er et hash array, også kalt for dictionaries og associative arrays.
Dette er array hvor hvert element består av en nøkkel og en verdi. Vi kan da benytte nøkkelen til å gjøre oppslag, istedet for indeks nummeret slik vi må på vanlige array. La oss ta eksemplet hvor vi trenger å lagre salgstall for tre avdelinger:
Avdeling Salgstall
Trondheim 200 000
Oslo 100 000
Bodø 150 000
Vi kan nå deklarere dette i PowerShell på følgende måte:
$salgstall = @{“Trondheim”=200000;”Oslo”=100000;”Bodø”=150000}
Vi kan nå aksesserer tallene for Trondheim og Bodø gjennom følgende kode:
$salgstall.Trondheim
$salgstall.Bodø
Merk! Hvis nøkkelen inneholder blant annet mellomrom eller punktum må vi ha ” rundt ordet, slik $salgstall.”Trondheim”
En av de store fordelene med PowerShell er objektorienteringen. Hva betyr så dette?
Med objektorientert mener vi at data og handlinger henger sammen. La oss se på følgende enkle eksempel:
”Donald Duck i store bokstaver” er en string, og man har derfor anledning til å kalle alle metoder som hører til på en string. Eksemplet viser godt hvordan data og handlinger henger sammen.
I objektorientering møter du to typer metoder som kan påkalles. (Handlinger altså) Dette er statiske metoder og instansmetoder. Instansmetoder påkalles med punktum mellom objektet og metoden. Statiske metoder påkalles med :: mellom objektet og metoden.
Eksempel på bruk av statisk metode:
Du kan hente ut statiske metoder fra objekttyper ved å pipe dem til get-member –static:
[string] | get-member –static
For å hente ut instansmetoder kan du kalle get-member på en instans:
“Donald Duck” | get-member
La oss se på ulike operatører i PoweShell. Vi starter med ulike sammenligningsoperatører. Ofte har vi behov for å sjekke om en variabel har en bestemt verdi, er større enn, mindre enn, eller ikke er en bestemt verdi.
|
Operatør
|
Beskrivelse
|
Eksempel
|
Resultat
|
|
-eq –ceq –ieq
|
Er lik
|
3 –eq 3
|
$true
|
|
-ne –cne –ine
|
Er ikke lik
|
3 –ne 3
|
$false
|
|
-gt –cgt –ige
|
Større enn
|
10 –gt 7
|
$true
|
|
-lt –clt –ilt
|
Mindre enn
|
10 –lt 7
|
$false
|
|
-ge –cge –ige
|
Større eller lik
|
10 –ge 7
|
$true
|
|
-le –cle –ile
|
Mindre enn eller lik
|
10 –le 7
|
$false
|
|
-contains
-ccontains
-icontains
|
Samlingen på venstre side inneholder verdien på høyre side
|
1,2,3 –contains 2
|
$true
|
|
-notcontains
-cnotcontains
-inotcontains
|
Samlingen på venstre siden inneholder IKKE verdien på høyre side
|
1,2,3 –notcontains 2
|
$false
|
En annen type operatører er de som setter verdier:
|
Operatør
|
Beskrivelse
|
Eksempel
|
Tilsvarer
|
|
=
|
Setter variabelen til en bestemt verdi
|
$a = 9
|
|
|
+=
|
Legger til eksisterende verdi, og setter variabelen lik den nye verdien
|
$a+=2
|
$a=$a+2
|
|
-=
|
Trekker verdi på høyreside ifra eksisterende verdi, og setter variabelen lik den nye verdien
|
$a-=2
|
$a=$a-2
|
|
*=
|
Multipliserer verdi på høyreside med eksisterende verdi, og setter variabelen lik den nye verdien
|
$a*=2
|
$a=$a*2
|
|
/=
|
Deler verdi på høyresiden med eksisterende verdi, og setter variabelen lik den nye verdien
|
$a/=2
|
$a=$a / 2
|
|
%=
|
Utfører heltallsdivisjon, og gir oss rest etter utført operasjon
|
$a%=2
|
$a=$a % 2
|
Et særtrekk ved PowerShell jeg ønsker du skal kjenne til er at man kan sette flere verdier på en effektiv måte. Noen ganger kan du ha behov for å bytte verdi mellom to variabler, og vanligvis skriver man det slik:
$midlertidig = $a
$a=$b
$b=$midlertidig
Eksemplet bytter verdiene i $a og $b. I PowerShell kan dette skrives på følgende måte:
$a,$b = $b,$a
La oss se på et eksempel som utnytter dette på en effektiv måte. Fibonacci rekken er en populær del av matematikken . Dette er en rekke med tall, hvor hvert element er summen av de to foregående.
Fibonacci rekken: 1 1 2 3 5 8 13 21
$c=$p=1;while ($c -lt 1000) {$c;$c,$p=($c+$p),$c}
Eksemplet skriver ut Fibonacci rekken til 1000. Hvert tall som skrives ut er lik summen av de to foregående tallene i rekken. Semikolonet ( ; ) benyttes til å avslutte en setning på lik linje med linjeskift. Vi starter med å sette $c og $p lik 1. Deretter lager vi en løkke (while) som kjører så lenge c er mindre enn 1000. Deretter skriver vi ut $c, og setter $c lik ($c+$p), og vi setter $p lik c$ (altså som verdien til $c før vi legger til $p).
Dette kan du også teste på en enkel måte:
$a=$b=1 # denne linjen setter bade $a og $b til 1
$a,$b = ($a+$b),$a # setter $a lik ($a+$b) og $b settes lik $a
$a
$b
Regular expressions er “wildcards” på steroider. Det er med andre ord vesentlig mer kraftfullt en standard “wildcard”.
Regular Expressions (heretter kalt for regex) er kraftfullt, og vi skal her se på hvordan du kan komme igang med det. De operatørene som virker med regex er –match og –replace, og varianter av disse, slik som –cmatch,-imatch,-notmatch,-cnotmatch,-inotmatch, -creplace og –ireplace. C variantene står for case sensitiv, altså følsomme for store og små bokstaver, mens i variantene er ikke sensitive på store og små bokstaver.
Et godt eksempel på en problemstilling regex løser er å hente ut alle ip-adressene fra for eksempel ei logfil. Vi vet ikke nødvendigvis hvor i dokumentet ip-adressene er lagret, men vi vet hvilken form de har, og kan benytte regex til å gjenkjenne dette mønsteret, for så å hente det ut.
For å forstå regex trenger du å kjenne til noen spesialtegn som benyttes :
|
Tegn
|
Beskrivelse
|
|
\w
|
Tilsvarer alfanumeriske tegn eller underscore ( _ )
|
|
\d
|
Tilsvarer siffer (0-9)
|
|
\t
|
Tilsvarer TAB (tabulator)
|
|
\s
|
Tilsvarer mellomrom, tabulator eller newline
|
Ved å benytte stor bokstav istedet for liten blir meningen motsatt. Altså, \D betyr ikke siffer, og \W betyr ikke et alfanumerisk tegn eller underscore. Merk at dette ikke er et fullstendig uttrekk av spesialtegn, men de du oftest får behov for å kunne.
I tillegg til tegnidentifikatorene må du kjenne noen formateringsregler.
|
Format
|
Beskrivelse
|
Eksempel
|
|
Verdi
|
Matcher eksakt tegnene i sammenligningsverdi hvor som helst i den orginale verdien vi sammenligner med
|
”PowerShell” –match ”Shell”
|
|
.
|
Matcher et enkelt tegn
|
|
|
[verdi]
|
Matcher minst et av tegnene i verdi
|
”PowerShell” –match ”[def]”
|
|
[range]
|
Matcher minst et av tegnene i området som er oppgitt
|
|
|
[^]
|
Matcher alle tegn, bortsett fra de i klammeparentes
|
|
|
^
|
Matcher starten av orginal verdi
|
”PowerShell” –match ”^power”
|
|
$
|
Matcher slutten av orginal verdi
|
”PowerShell” –match ”ell$”
|
|
*
|
Matcher null eller flere forekomster av etterfølgende tegn
|
”PowerShell” –match ”\w*”
|
|
?
|
Matcher null eller en forekomster av etterfølgende tegn
|
|
|
\
|
Matcher etterfølgende tegn som et ”escape” tegn
|
|
|
+
|
Matcher repeterende forekomster av angitte mønster
|
|
|
{n}
|
Spesifiserer eksakt n forekomster
|
|
|
{n,}
|
Spesifiserer minst n forekomster
|
|
|
{n,m}
|
Spesifiserer minst n, men ikke mer enn m forekomster
|
|
Resultatet av en regex operasjon lagres automatisk i variablen $matches.
“PowerShell rules!” –match “\w* \w*”
$matches
Følgende eksempel krever at det er to ord for å gi true (gir false på ett ord og mer enn to):
“PowerShell rules!” –match “^\w* \w*$”
La oss benytte PowerShell til å sjekke om teksten vår er tall:
“PowerShell rules!” –match “\d”
Prøv nå følgende:
“123” –match “\d”
Det første eksemplet skal returnere false, da det ikke er et tall. I det andre eksemplet får du returnert true, ettersom at det er tall.
La oss nå sjekke om noe er en ip-adresse. VI begynner enkelt med å sjekke for 4 grupper med tall, adskilt med punktum:
"10.1.1.200" -match "^\d+\.\d+\.\d+\.\d+$"
"10.1.1" -match "^\d+\.\d+\.\d+\.\d+$"
Videre vet vi at en IP-adresse kun kan bestå at tre tall i hver gruppe. La oss erstatte + med {1.3}. Som ser av tabellen over betyr {1,3} minst en forekomst, og maks 3. Med \d{1,3} sier vi altså tall, minimum et tegn, og maksimum 3 tegn langt.
"10.1.1.200" -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
La oss anvende dette i praksis med følgende eksempel.
Vi kjører kommandoen net config workstation og oppdager at linje 2 i resultatet inneholder maskinnavnet. Net config workstation er en gammel ”cmd.exe” kommando, og er ikke en PowerShell kommando som gir oss objekter å jobbe med. Vi får kun tekst i resultatet. La oss nå benytte regex til å hente ut maskinnavnet :
Først kjører vi kommandoen for å se på utdataene.
Deretter bestemmer vi oss for å lese ut linje nr 2. Reultatet er allerede et array med strenger for hver linje, og vi vet at alle indekser i PowerShell starter med 0. Det betyr at linje nr 2, adresseres med 1 i indeksen:
La oss opprette regex uttrykket vi skal sjekke mot i en variabel:
Hvis maskinen er medlem i et domene benytte du følgende uttrykk:
$expr = ‘^Full Computer.* [^.]+\.[^.]+’
Hvis maskinen ikke er medlem i et domene, benytter du følgende uttrykk:
$expr = ‘^Full Computer.* [^.]+’
Ved å benytte uttrykket vi nettopp opprettet ($expr) så trenger vi ikke å vite hvilken linje det ligger på. Vi sender inn uttrykket, og får linjen vi søker tilbake.
(net config workstation) –match $expr
Ved å benytte regex uttrykk kan vi unngå å måtte ”lete igjennom” en tekst for det vi ønsker å finne. En tradisjonell krevende måte å gjøre slike operasjoner på er å ”løkke” igjennom linjen, og sammenligne på leting, eventuellt så regner man seg frem til posisjon, og leser teksten basert på posisjon. Når vi jobber med variable lengder på det vi skal ha er det krevende.
En annen typisk operasjon er å finne bestemte data i en samling med ustrukturerte data. Med ustrukturert mener jeg slik som en tekst, hvor ikke all informasjon ligger i dedikerte felt, slik som for eksempel i en database.
La oss si at vi ønsker å finne IP adressen i følgende setning
Finn IP adressen min 210.12.34.19 hvis du kan
"Finn IP adressen min 210.12.34.19 hvis du kan" -match "\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}"
Sjekk nå om hva som ligger i $matches variabelen:
$matches
Du vil da finne IP-adressen.
Et sentralt konsept innen script og programmering er løkker og valg. I mange tilfeller har vi behov for å iterere over flere verdier, gjerne også for å sammenligne verdier med en bestemt verdi for å avgjøre hva vi skal gjøre. Etterhvert som et script utføres må det ofte gjøre valg for hva det skal gjøre basert på sammenligninger.
La oss starte med en enkel løkke som itererer over 0 til 9:
for ( $i = 0; $i -lt 10; $i+=1 ) { $i }
Formatet er slik : for (initsiering av verdi; tilstand for å utføre løkken; endring av verdi)
Vi lager oss en for løkke med følgende egenskaper:
Variabelen $i settes til 0.
Utfør løkken så lenge $i er mindre enn 10
Øk $i med 1 for hver iterasjon
Med andre ord kjøres løkken ti ganger, fra 0 til 9. Vi kunne fått den til å kjøre 11 ganger, fra 0 til 10 ved å endret –lt til –le.
La oss ta eksemplet videre og lage en multiplikasjonstabell (skriv alt på en line):
for ( $i = 1; $i -lt 10; $i+=1 ) { for ($x=1;$x -lt 10;$x+=1){ write-host `t($i*$x) -nonewline} write-host }
Her kjører vi to for løkker, en inne i den andre.Den ytre løkken teller fra 1 til 9. For hver av tallene som telles opp, teller den indre løkken fra 1 til 9, og vi multipliserer dermed hver av de ytre verdiene med 1 til 9. Jeg bruker også –nonewline for at tallene skal komme fortløpende på samme linje, og ikke skrives ut på hver sin linje. For formateringens skyld kjører jeg også `t foran hver, slik at det blir en tabulator mellom hvert resultat.
En annen løkke vi har er while. Der for løkken er kjekk å bruke når du vet hvor mange ganger du skal kjøre løkken (hvor mange ganger du skal iterere), så er while løkkens fordel at den står friere i antall kjøringer. Den kan for eksempel kjøre til noe blir sant eller noe kommer til enden.
While løkken er på mange måter den enkleste å mestre, og på mange måter den mest kraftfulle. Hver type loop har sin egen oppbygning, og While løkkens oppbygning inneholder to elementer; betingelse og kode. Som ellers bruker vi ulike typer parenteser, vanlig parenteser () for betingelse og klammeparentesene { } for koden.
I en while løkke blir betingelsene sjekket før starten av hver gjennomkjøring, og hvis betingelsen er sann, blir koden utført.
Eksempel på while løkke
$i =7
while ($i -le 85) { $i; $i +=7}
Eksemplet skriver ut syvgangen opp til 85.
Merk at kodeblokken { $i ; $i +=7 }benytter semikolon (;) for å skille kodelinjer fra hverandre. Det betyr med andre ord at kodeblokken består av to separate linjer, linje 1 med $i og linje 2 med $i+=7. Den første linjen skriver ut verdien av $i, og den andre øker $i med syv.
En Do … While løkke skiller seg fra While løkken med at betingelsen sjekkes på slutten av løkken. Dette sikrer at en Do … While løkke alltid blir utført minst en gang, da while kommer etter do
$i = 7; do { $i; $i +=7 } while ($i -le 85)
Do … While løkken over skriver også ut syvgangen til 85.
Do .. Until løkken er en variant av Do..While løkken hvor logikken er endret.En Do..Until løkke utføres til betingelsen er sann, da avslutter den. En Do...While løkke utføres så lenge betingelsen er sann, men avslutter i det øyeblikket den ikke er sann.
$i = 7; do { $i; $i +=7 } until ($i -gt 85)
Foreach løkker inneholder flere valgmuligheter enn de andre, og benyttes ofte i PowerShell.
Hovedfunksjonaliteten i en foreach løkke er at den gjennomgår alle elementene i et array (collection, eller samling med flere verdier om du vil). Legg spesielt merke til bruken av ordet in.
Foreach($element in $collection) { utfør dette }
Syvgangen i en foreach løkke presenteres på følgende måte:
foreach ($number in 1,2,3,4,5,6,7,8,9,10,11,12 ) { $number * 7}
En annen måte å skrive dette på (vi snakker jo tross alt PowerShell) er som følger:
$arr=1..12
foreach($number in $arr){ $number *7 }
For å vise hvor kompakt dette kan skrives i PowerShell - se på dette eksempelet:
$o = 1..12 |% {$_ * 7 }
$o
Merk! % er et alias for foreach
Her er et eksempel på løkke i pipeline. Vi henter ut alle filer med endelsen mp3, og itererer over dem. For hver fil erstatter vi mellomrom ” ” med underscore _ ,
get-childitem *.mp3 | foreach { rename-item $_ $_.Name.Replace(” “, “_”) }
Til å gjøre valg har vi to sentrale metoder, if og switch.
If sammenligner to verdier på den måten vi angir. Hvis utrykket vi sammenligner blir sant, kjøres koden i if setningen. Hvis ikke, går den videre. En if setning kan også ha en else del, som blir kjørt hvis if uttrykket blir usant.
La oss se på et enkelt eksempel:
IfDemo.ps1
[int]$i = Read-Host “Tast inn et tall”
if($i –le 10)
{
Write-host ”Mindre enn 10”
}
Elseif($i –le 50)
{
Write-host ”Mellom 10 og 50”
}
Else
{
Write-host ”Større enn 50”
}
If setninger er best egnet til å sammenligne noe med en eller veldig få verdier. Skal man sammenligne flere verdier bør man bruke switch:
[int]$var=read-host ”Tast inn et tall mellom 1 og 6”
switch ($var)
{
1 {”Option 1”}
2 {”Option 2”}
3 {”Option 3”}
4 {”Option 4”}
5 {”Option 5”}
6 {”Option 6”}
Default {“Ingen treff”}
}
Med switch har vi et sett med sammenligninger vi utfører ( i tilfellet over 1,2,3,4,5,6 – samt default som tar alle de verdiene vi ikke har tatt høyde for). Hvis uttrykket stemmer, for eksempel at vi har tastet inn 3, og vi faktisk har en sammenligning for 3, så kjøres kodeblokken som står bak 3. I dette tilfellet er det { ”Option 3” }.
Sammen med switch kan vi pgså bruke noen parametre:
Tabell 4 Switch parametre
|
Parameter
|
Beskrivelse
|
|
-regex
|
Gjør at tekststrengen vi sender inn blir behandlet som en regular expression string, og at vi kan benytte regex uttrykk for å sammenligne verdiene. Merk at denne ikke kan kombineres med wildcard og exact
|
|
-wildcard
|
Matcher strengen vi sender inn som mot alternativene vi tester mot, men med wildcard (slik at det ikke trenger å være eksakt treff)
|
|
-exact
|
Krever at det er et eksakt treff
|
|
-casesensitive
|
Standard er at den IKKE skiller mellom store og små bokstaver. Hvis du ønsker at den skal skille, må du benytte –casesensitive
|
|
-file
|
Tar input fra fil istedet for direkte. Hver linje i fila blir sjekket opp mot sammenligningsgrunnlaget i switch setninga.
|
La oss se på et eksempel med regex :
SwitchRegex.ps1
$var=”PowerShell123”,”PowerShell”,”123”,”PowerShell 123”
Switch –regex ($var)
{
“^\w+[a-zA-Z]$” { write-host $_” er et ord “}
”^\d+$” { write-host $_” er et tall ”}
”\s” { write-host $_” har et mellomrom ” }
Default { write-host ”Ingen treff for ”$_}
}
Hvis det kan være flere treff i sammenligningene, kan man påvirke om man skal fortsette å sjekke etter å ha fått et treff, eller om man skal avslutte. Dette gjøres med Break og Continue.
Dette merkes med ; Continue eller ; Break. Følgende endringer i scriptet medfører at det fortsetter:
SwitchRegexContinue.ps1
$var=”PowerShell123”,”PowerShell”,”123”,”PowerShell 123”
Switch –regex ($var)
{
“^\w+[a-zA-Z]$” { write-host $_” er et ord “; continue}
”^\d+$” { write-host $_” er et tall ” ; continue}
”\s” { write-host $_” har et mellomrom ” ; continue}
Default { write-host ”Ingen treff for ”$_}
}
Nå skal vi se hvordan switch kan benyttes sammen med –file parameteren til å finne alle linjer med ei IP-adresse i teksten.
Opprett fila adresser.txt med følgende innhold:
Dette er ei linje med ip 12.19.54.123
På denne linja har jeg 76.3.122.19
Denne linja har ingen IP
og denne linja har 10.10.10.10
Denne linja derimot har 201.201.233.24
Hva skal vi med alle disse linjene?
Sørg for at du har lagret fila, og kjør nå følgende kommando i PowerShell:
switch –file addresser.txt –regex {
“\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}” { $_ }
}
Resultatet skal nå se slik ut:
Ønsker du å kun se IP-adressene?
switch -file ustrukturert.txt -regex {
"\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}" {$_ -match "\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}" | out-null;$matches.values }
}
I dette eksemplet kjører vi to regular expressions – først for å hente ut de linjene som inneholder en IP-adresse, deretter en for å hente kun IP-adressen ut av disse. Jeg kjører pipe to null (out-null) for å skjule true/false som kommer ut av uttrykket ved kjøring (prøv å ta det bort –så ser du hva jeg mener).
Vi har altså hentet ut de linjene som inneholder IP-adresser. Kraftfullt, eller skal jeg heller si Powerfull?
Vi har i denne delen sett litt på variabler, datatyper, løkker og valg. I tillegg har vi sett litt på regular expressions.