Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Allt du ville veta om instruktionen
Liksom många andra språk har PowerShell instruktioner för villkorlig körning av kod i dina skript. En av dessa instruktioner är If-instruktionen . Idag tar vi en djupdykning i ett av de mest grundläggande kommandona i PowerShell.
Anmärkning
Den ursprungliga versionen av den här artikeln publicerades på bloggen som skrevs av @KevinMarquette. PowerShell-teamet tackar Kevin för att ha delat det här innehållet med oss. Kolla in hans blogg på PowerShellExplained.com.
Villkorsstyrd körning
Dina skript måste ofta fatta beslut och utföra olika logik baserat på dessa beslut.
Detta är vad jag menar med betingad körning. Du har en instruktion eller ett värde att utvärdera och kör sedan ett annat kodavsnitt baserat på utvärderingen. Det här är precis vad instruktionen if
gör.
Påståendet if
Här är ett grundläggande exempel på instruktionen if
:
$condition = $true
if ( $condition )
{
Write-Output "The condition was true"
}
Den första saken if
gör är att utvärdera uttrycket inom parentesen. Om den utvärderas till $true
, så körs scriptblock
i klammerparenteserna. Om värdet var $false
skulle det hoppa över skriptblocket.
I föregående exempel utvärderade instruktionen if
bara variabeln $condition
. Det var $true
och skulle ha kört Write-Output
-kommandot i skriptblocket.
På vissa språk kan du placera en enda kodrad efter -instruktionen if
och den körs. Så är inte fallet i PowerShell. Du måste ange en fullständig scriptblock
med klammerparenteser för att den ska fungera korrekt.
Jämförelseoperatorer
Den vanligaste användningen av if
är att jämföra två objekt med varandra. PowerShell har särskilda operatorer för olika jämförelsescenarier. När du använder en jämförelseoperator jämförs värdet till vänster med värdet till höger.
-eq för likhet
-eq
Gör en likhetskontroll mellan två värden för att se till att de är lika med varandra.
$value = Get-MysteryValue
if ( 5 -eq $value )
{
# do something
}
I det här exemplet tar jag ett känt värde för 5
och jämför det med mitt $value
för att se om de matchar.
Ett möjligt användningsfall är att kontrollera statusen för ett värde innan du vidtar en åtgärd för det. Du kan hämta en tjänst och kontrollera att statusen var igång innan du anropade Restart-Service
den.
Det är vanligt på andra språk som C# att använda ==
för likhet (t.ex. 5 == $value
) men det fungerar inte med PowerShell. Ett annat vanligt misstag som människor gör är att använda likhetstecknet (t.ex. 5 = $value
) som är reserverat för att tilldela värden till variabler. Genom att placera ditt kända värde till vänster gör det misstaget mer besvärligt att göra.
Den här operatorn (och andra) har några olika varianter.
-
-eq
skiftlägesokänslig likhet -
-ieq
skiftlägesokänslig likhet -
-ceq
skiftlägeskänslig likhet
-ne inte lika med
Många operatorer har en relaterad operator som söker efter det motsatta resultatet.
-ne
verifierar att värdena inte är lika med varandra.
if ( 5 -ne $value )
{
# do something
}
Använd detta för att se till att åtgärden bara körs om värdet inte är 5
. Ett bra användningsfall där skulle vara att kontrollera om en tjänst var i körningstillstånd innan du försöker starta den.
Variationer:
-
-ne
skiftlägesokänsligt inte lika med -
-ine
skiftlägesokänsligt inte lika med -
-cne
skiftlägeskänslig ej lika med
Det här är invertera varianter av -eq
. Jag grupperar dessa typer när jag listar varianter för andra operatorer.
-gt -ge -lt -le för större än eller mindre än
Dessa operatorer används när du kontrollerar om ett värde är större eller mindre än ett annat värde.
The -gt -ge -lt -le
står för StörreÄn, StörreÄnEllerLika, MindreÄn och MindreÄnEllerLika.
if ( $value -gt 5 )
{
# do something
}
Variationer:
-
-gt
större än -
-igt
större än, skiftlägesokänslig -
-cgt
större än, skiftlägeskänslig -
-ge
större än eller lika med -
-ige
större än eller lika med skiftlägesokänslig -
-cge
större än eller lika med skiftlägeskänslig -
-lt
mindre än -
-ilt
mindre än, skiftlägesokänslig -
-clt
mindre än, skiftlägesberoende -
-le
mindre än eller lika med -
-ile
mindre än eller lika med, inte skiftlägeskänslig -
-cle
mindre än eller lika med, skiftlägeskänsligt
Jag vet inte varför du skulle använda skiftlägeskänsliga och okänsliga alternativ för dessa operatörer.
-som jokerteckenmatchningar
PowerShell har en egen jokerteckenbaserad mönstermatchningssyntax och du kan använda den med operatorn -like
. Dessa jokerteckenmönster är ganska grundläggande.
-
?
matchar ett enskilt tecken -
*
matchar valfritt antal tecken
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
# do something
}
Det är viktigt att påpeka att mönstret matchar hela strängen. Om du behöver matcha något i mitten av strängen, måste du placera *
i båda ändarna av strängen.
$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
# do something
}
Variationer:
-
-like
icke-skiftlägeskänsligt jokertecken -
-ilike
skiftlägesokänsligt wildcard-tecken -
-clike
skiftlägeskänsligt jokertecken -
-notlike
skiftlägesokänsligt jokertecken matchas inte -
-inotlike
skiftlägesokänsligt jokertecken matchas inte -
-cnotlike
skiftlägeskänslig wildcard matchas inte
-matcha reguljära uttryck
Med -match
operatorn kan du kontrollera en sträng för en reguljär uttrycksbaserad matchning. Använd detta när jokertecknen inte är tillräckligt flexibla för dina behov.
$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
# do something
}
Ett regexmönster matchar var som helst i strängen som standard. Så du kan ange en delsträng som du vill matcha så här:
$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
# do something
}
Regex är ett eget komplext språk och värt att titta på. Jag talar mer om -match
och många sätt att använda regex i en annan artikel.
Variationer:
-
-match
skiftlägesokänsligt regex -
-imatch
skiftlägesokänsligt regex -
-cmatch
skiftlägeskänslig regex -
-notmatch
skiftlägesokänsligt regex matchades inte -
-inotmatch
skiftlägesokänsligt reguljärt uttryck matchades inte -
-cnotmatch
skiftlägeskänslig regex matchades inte
-är av typen
Du kan kontrollera värdets typ med operatorn -is
.
if ( $value -is [string] )
{
# do something
}
Du kan använda detta om du arbetar med klasser eller accepterar olika objekt över pipelinen. Du kan ha antingen en tjänst eller ett tjänstnamn som indata. Kontrollera sedan om du har en tjänst, och hämta den om du bara har namnet.
if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
$Service = Get-Service -Name $Service
}
Variationer:
-
-is
av typen -
-isnot
inte av typen
Insamlingsoperatorer
När du använder de tidigare operatorerna med ett enda värde blir $true
resultatet eller $false
. Detta hanteras något annorlunda när du arbetar med en samling. Varje objekt i samlingen utvärderas och operatorn returnerar varje värde som utvärderas till $true
.
PS> 1,2,3,4 -eq 3
3
Detta fungerar fortfarande korrekt i en if
-instruktion. Så returneras ett värde av din operator, och sedan är hela $true
-instruktionen.
$array = 1..6
if ( $array -gt 3 )
{
# do something
}
Det finns en liten fälla som gömmer sig i detaljerna här som jag måste påpeka. När du använder operatorn -ne
på det här sättet är det enkelt att av misstag titta på logiken bakåt. Användning -ne
med en samling returnerar $true
om något objekt i samlingen inte matchar ditt värde.
PS> 1,2,3 -ne 4
1
2
3
Detta kan se ut som ett smart trick, men vi har operatörer -contains
och -in
som hanterar detta mer effektivt. Och -notcontains
gör vad du förväntar dig.
-innehåller
Operatorn -contains
kontrollerar samlingen för det angivna värdet. Så fort den hittar en matchning returnerar $true
.
$array = 1..6
if ( $array -contains 3 )
{
# do something
}
Det här är det bästa sättet att se om en samling innehåller ditt värde. Att använda Where-Object
(eller -eq
) innebär att hela listan genomgås varje gång och det är betydligt långsammare.
Variationer:
-
-contains
skiftlägesokänslig jämförelse -
-icontains
skiftlägesokänslig matchning -
-ccontains
skiftlägeskänslig matchning -
-notcontains
skiftlägesokänsligt inte matchat -
-inotcontains
inte matchat skiftlägesokänsligt -
-cnotcontains
skiftlägeskänslig inte matchad
-i
Operatorn -in
är precis som operatorn -contains
förutom att samlingen finns till höger.
$array = 1..6
if ( 3 -in $array )
{
# do something
}
Variationer:
-
-in
skiftlägesokänslig matchning -
-iin
skiftlägesokänslig matchning -
-cin
skiftlägeskänslig matchning -
-notin
skiftlägesokänsligt inte matchat -
-inotin
skiftlägesokänsligt matchade inte -
-cnotin
skiftlägeskänslig matchas inte
Logiska operatorer
Logiska operatorer används för att invertera eller kombinera andra uttryck.
-inte
Operatorn -not
vänder ett uttryck från $false
till $true
eller från $true
till $false
. Här är ett exempel där vi vill utföra en åtgärd när Test-Path
är $false
.
if ( -not ( Test-Path -Path $path ) )
De flesta av de operatorer vi pratade om har en variant där du inte behöver använda operatorn -not
. Men det finns fortfarande tillfällen då det är användbart.
! operatör
Du kan använda !
som ett alias för -not
.
if ( -not $value ){}
if ( !$value ){}
Du kan se !
användas mer av personer med kunskap i andra programmeringsspråk, som C#. Jag föredrar att skriva ut det eftersom jag har svårt att se när jag snabbt tittar på mina skript.
-och
Du kan kombinera uttryck med operatorn -and
. När du gör det måste båda sidor vara $true
för att hela uttrycket ska vara $true
.
if ( ($age -gt 13) -and ($age -lt 55) )
I det exemplet $age
måste vara 13 år eller äldre för vänster sida och mindre än 55 för höger sida. Jag har lagt till extra parenteser för att göra det tydligare i det exemplet, men de är valfria så länge uttrycket är enkelt. Här är samma exempel utan dem.
if ( $age -gt 13 -and $age -lt 55 )
Utvärderingen sker från vänster till höger. Om det första objektet utvärderas till $false
avslutas det tidigt och utför inte rätt jämförelse. Det här är praktiskt när du behöver se till att det finns ett värde innan du använder det. Till exempel Test-Path
genererar ett fel om du ger den en $null
sökväg.
if ( $null -ne $path -and (Test-Path -Path $path) )
-eller
Gör -or
att du kan ange två uttryck och returnerar $true
om någon av dem är $true
.
if ( $age -le 13 -or $age -ge 55 )
Precis som med operatorn -and
sker utvärderingen från vänster till höger. Förutom om den första delen är $true
, så är $true
hela instruktionen och resten av uttrycket bearbetas inte.
Anteckna också hur syntaxen fungerar för dessa operatorer. Du behöver två separata uttryck. Jag har sett användare försöka göra något liknande utan $value -eq 5 -or 6
att inse sitt misstag.
-xor exklusivt eller
Den här är lite ovanlig.
-xor
tillåter endast ett uttryck att utvärdera till $true
. Så om båda objekten är $false
eller båda objekten är $true
, så är hela uttrycket $false
. Ett annat sätt att se på detta är att uttrycket endast är $true
när resultaten av uttrycket är olika.
Det är sällsynt att någon någonsin skulle använda denna logiska operatör och jag kan inte komma på ett bra exempel på varför jag någonsin skulle använda den.
Bitvisa operatörer
Bitvis-operatorer utför beräkningar på bitarna i värdena och skapar ett nytt värde som resultat. Att undervisa om bitvis-operatorer ligger utanför denna artikels omfång, men här är listan över dem.
-
-band
binär OCH -
-bor
binärt ELLER -
-bxor
binärt exklusivt eller -
-bnot
binärt INTE -
-shl
skift vänster -
-shr
skift höger
PowerShell-uttryck
Vi kan använda vanlig PowerShell i villkorssatsen.
if ( Test-Path -Path $Path )
Test-Path
returnerar $true
eller $false
när den körs. Detta gäller även kommandon som returnerar andra värden.
if ( Get-Process Notepad* )
Den utvärderas till $true
om en returnerad process finns och $false
om ingen finns. Det är helt giltigt att använda pipelineuttryck eller andra PowerShell-instruktioner som detta:
if ( Get-Process | where Name -EQ Notepad )
Dessa uttryck kan kombineras med varandra med operatorerna -and
och -or
, men du kan behöva använda parenteser för att dela upp dem i underuttryck.
if ( (Get-Process) -and (Get-Service) )
Söker efter $null
Om det inte finns något resultat eller om ett $null
-värde utvärderas som $false
i if
-satsen. När du söker specifikt efter $null
är det en bra idé att placera den $null
på vänster sida.
if ( $null -eq $value )
Det finns en hel del nyanser när du hanterar $null
värden i PowerShell. Om du är intresserad av att dyka djupare, har jag en artikel om allt du ville veta om $null.
Variabeltilldelning inom villkoret
Jag glömde nästan att lägga till den här tills Prasoon Karunan V påminde mig om det.
if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}
Normalt när du tilldelar ett värde till en variabel skickas inte värdet till pipelinen eller konsolen. När du gör en variabeltilldelning i ett underuttryck skickas den vidare till pipelinen.
PS> $first = 1
PS> ($second = 2)
2
Se hur tilldelningen $first
inte har några utdata och tilldelningen $second
gör det? När en tilldelning görs i en if
-instruktion körs den precis som tilldelningen $second
ovan. Här är ett rent exempel på hur du kan använda det:
if ( $process = Get-Process Notepad* )
{
$process | Stop-Process
}
Om $process
tilldelas ett värde, är $true
uttalandet och $process
stoppas.
Se till att du inte förväxlar detta med -eq
eftersom detta inte är en likhetskontroll. Det här är en mer obskyr funktion som de flesta inte inser fungerar på det här sättet.
Variabeltilldelning från skriptblock
Du kan också använda instruktionen if
scriptblock för att tilldela ett värde till en variabel.
$discount = if ( $age -ge 55 )
{
Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
Get-ChildDiscount
}
else
{
0.00
}
Varje skriptblock skriver resultatet av kommandona, eller värdet, som utdata. Vi kan tilldela resultatet av -instruktionen if
till variabeln $discount
. Det exemplet kan lika enkelt ha tilldelat dessa värden till variabeln $discount
direkt i varje scriptblock. Jag kan inte säga att jag använder detta med instruktionen if
ofta, men jag har ett exempel där jag använde detta nyligen.
Alternativ körningsväg
Med -instruktionen if
kan du ange en åtgärd för inte bara när -instruktionen är $true
, utan även för när den är $false
. Det är här instruktionen else
kommer in i bilden.
annars
Instruktionen else
är alltid den sista delen av instruktionen if
när den används.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
Write-Warning "$path doesn't exist or isn't a file."
}
I det här exemplet kontrollerar $path
vi att det är en fil. Om vi hittar filen flyttar vi den. Annars skriver vi en varning. Den här typen av förgreningslogik är mycket vanlig.
Kapslad om
if
- och else
-satserna tar ett skriptblock, så vi kan placera alla PowerShell-kommandon i dem, inklusive en annan if
-sats. På så sätt kan du använda mycket mer komplicerad logik.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
if ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
}
I det här exemplet testar vi framgångsvägen först och vidtar sedan åtgärder utifrån resultaten. Om det misslyckas gör vi en annan kontroll och tillhandahåller mer detaljerad information till användaren.
elseif
Vi är inte begränsade till bara en enda villkorsstyrd kontroll. Vi kan länka ihop if
- och else
-instruktioner i stället för att kapsla dem med hjälp av instruktionen elseif
.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
elseif ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
Exekveringen sker uppifrån och ned. Den översta if
instruktionen utvärderas först. Om det är $false
flyttas den ned till nästa elseif
eller else
i listan. Den sista else
är standardåtgärden att vidta om ingen av de andra returnerar $true
.
strömbrytare
I det här läget måste jag nämna switch
uttalandet. Det ger en alternativ syntax för att göra flera jämförelser med ett värde.
switch
Med anger du ett uttryck och det resultatet jämförs med flera olika värden. Om ett av dessa värden matchar körs det matchande kodblocket. Ta en titt på det här exemplet:
$itemType = 'Role'
switch ( $itemType )
{
'Component'
{
'is a component'
}
'Role'
{
'is a role'
}
'Location'
{
'is a location'
}
}
Det finns tre möjliga värden som kan matcha $itemType
. I det här fallet matchar det med Role
. Jag använde ett enkelt exempel bara för att ge dig viss exponering för operatören switch
. Jag pratar mer om allt du någonsin ville veta om switch-instruktionen i en annan artikel.
Array inlinje
Jag har en funktion med namnet Invoke-SnowSql som startar en körbar fil med flera kommandoradsargument. Här är ett klipp från den funktionen där jag skapar argumentmatrisen.
$snowSqlParam = @(
'--accountname', $Endpoint
'--username', $Credential.UserName
'--option', 'exit_on_error=true'
'--option', 'output_format=csv'
'--option', 'friendly=false'
'--option', 'timing=false'
if ($Debug)
{
'--option', 'log_level=DEBUG'
}
if ($Path)
{
'--filename', $Path
}
else
{
'--query', $singleLineQuery
}
)
Variablerna $Debug
och $Path
är parametrar för funktionen som tillhandahålls av slutanvändaren.
Jag utvärderar dem inline i initialiseringen av min matris. Om $Debug
är sant hamnar $snowSqlParam
dessa värden på rätt plats. Samma gäller för variabeln $Path
.
Förenkla komplexa åtgärder
Det är oundvikligt att du stöter på en situation som har alldeles för många jämförelser att hantera och din if
sats rullar långt ut på höger sida av skärmen.
$user = Get-ADUser -Identity $UserName
if ( $null -ne $user -and $user.Department -eq 'Finance' -and $user.Title -match 'Senior' -and $user.HomeDrive -notlike '\\server\*' )
{
# Do Something
}
De kan vara svåra att läsa och det gör dig mer benägen att göra misstag. Det finns några saker vi kan göra åt det.
Radfortsättning
Det finns vissa operatorer i PowerShell som låter dig omsluta kommandot till nästa rad. De logiska operatorerna -and
och -or
är bra operatorer att använda om du vill dela upp uttrycket i flera rader.
if ($null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
)
{
# Do Something
}
Det är fortfarande mycket som händer där, men att placera varje pjäs på sin egen linje gör stor skillnad. Jag använder vanligtvis detta när jag får mer än två jämförelser eller om jag måste bläddra till höger för att läsa någon av logiken.
Förberäkna resultat
Vi kan ta bort påståendet från if
-uttrycket och bara kontrollera resultatet.
$needsSecureHomeDrive = $null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
if ( $needsSecureHomeDrive )
{
# Do Something
}
Detta känns bara mycket renare än i föregående exempel. Du får också möjlighet att använda ett variabelnamn som förklarar vad det är som du verkligen kontrollerar. Det här är också ett exempel på självdokumenteringskod som sparar onödiga kommentarer.
Flera if-instruktioner
Vi kan dela upp detta i flera påståenden och kontrollera dem en i taget. I det här fallet använder vi en flagga eller en spårningsvariabel för att kombinera resultaten.
$skipUser = $false
if( $null -eq $user )
{
$skipUser = $true
}
if( $user.Department -ne 'Finance' )
{
Write-Verbose "isn't in Finance department"
$skipUser = $true
}
if( $user.Title -match 'Senior' )
{
Write-Verbose "Doesn't have Senior title"
$skipUser = $true
}
if( $user.HomeDrive -like '\\server\*' )
{
Write-Verbose "Home drive already configured"
$skipUser = $true
}
if ( -not $skipUser )
{
# do something
}
Jag behövde invertera logiken för att få flagglogik att fungera korrekt. Varje utvärdering är ett enskilt if
påstående. Fördelen med detta är att när du felsöker kan du se exakt vad logiken gör. Jag kunde lägga till mycket bättre verbositet på samma gång.
Den uppenbara nackdelen är att det är så mycket mer kod att skriva. Koden är mer komplex att titta på eftersom den tar en enda logikrad och exploderar den i 25 eller fler rader.
Använda funktioner
Vi kan också flytta all valideringslogik till en funktion. Titta på hur rent det här ser ut vid en första anblick.
if ( Test-SecureDriveConfiguration -ADUser $user )
{
# do something
}
Du måste fortfarande skapa funktionen för att utföra valideringen, men den gör den här koden mycket enklare att arbeta med. Det gör den här koden enklare att testa. I dina test kan du mocka anropet till Test-ADDriveConfiguration
och du behöver bara två test för den här funktionen. En där den returnerar $true
och en där den returnerar $false
. Det är enklare att testa den andra funktionen eftersom den är så liten.
Den här funktionens brödtext kan fortfarande vara den enradare som vi började med eller den exploderande logik som vi använde i det sista avsnittet. Detta fungerar bra för båda scenarierna och gör att du enkelt kan ändra implementeringen senare.
Felhantering
En viktig användning av -instruktionen if
är att söka efter felvillkor innan du stöter på fel. Ett bra exempel är att kontrollera om det redan finns en mapp innan du försöker skapa den.
if ( -not (Test-Path -Path $folder) )
{
New-Item -Type Directory -Path $folder
}
Jag vill säga att om du förväntar dig att ett undantag ska hända, så är det egentligen inte ett undantag. Kontrollera dina värden och verifiera dina villkor där du kan.
Om du vill dyka lite mer i faktisk undantagshantering har jag en artikel om allt du någonsin velat veta om undantag.
Sista ord
Instruktionen if
är en så enkel instruktion men är en grundläggande del av PowerShell. Du kommer att använda detta flera gånger i nästan varje skript som du skriver. Jag hoppas att du har en bättre förståelse än du hade tidigare.