Dela via


Allt du ville veta om instruktionen if

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 $falseskulle 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 $falseavslutas 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 $falseflyttas 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. switchMed 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.