共用方式為


您想知道有關 if 語句的所有資訊

與許多其他語言一樣,PowerShell 具有語句,可有條件地在您的腳本中執行程序代碼。 其中一個語句是 if 的語句。 今天,我們將深入探討PowerShell中最基本的命令之一。

備註

本文的原始版本出現在@KevinMarquette撰寫的部落格上。 PowerShell 小組感謝 Kevin 與我們分享此內容。 請在 PowerShellExplained.com查看他的部落格。

條件式執行

您的腳本通常需要根據這些決策做出決策並執行不同的邏輯。 這就是我所謂的條件執行。 您有一個要評估的語句或值,然後根據該評估來執行不同的程式碼區段。 這正是 if 語句的用途。

if 陳述

以下是 if 語句的基本範例:

$condition = $true
if ( $condition )
{
    Write-Output "The condition was true"
}

if 語句所做的第一件事就是計算括號中的運算式。 如果計算結果為 $true,則會在大括號中執行 scriptblock。 如果值是 $false,則會略過該腳本區塊。

在上一個範例中,if 語句只是評估 $condition 變數。 它是 $true,並且會在腳本塊內執行 Write-Output 命令。

在某些語言中,您可以在 if 語句後面放置單行程序代碼,並執行它。 在 PowerShell 中,情況並非如此。 您必須提供一個完整的 scriptblock 包含大括弧,才能正常運作。

比較運算子

if 語句的最常見用法是將兩個項目進行比較。 PowerShell 具有適用於不同比較案例的特殊運算符。 當您使用比較運算符時,左側的值會與右側的值進行比較。

-eq for equality

-eq 會執行兩個值之間的相等檢查,以確保它們彼此相等。

$value = Get-MysteryValue
if ( 5 -eq $value )
{
    # do something
}

在此範例中,我採用 5 的已知值,並將它與我的 $value 進行比較,以查看它們是否相符。

其中一個可能的使用案例是先檢查值的狀態,再對它採取動作。 您可以取得服務,並檢查狀態是否正在執行,然後再呼叫 Restart-Service

C# 等其他語言通常會使用 == 進行相等(例如:5 == $value),但這不適用於 PowerShell。 人們常犯的另一個錯誤是使用等號(例如:5 = $value),這個等號通常是用來給變數賦值的。 藉由將已知的值放在左邊,可以降低犯錯的可能性。

這個運算子(以及其他運算子)有一些變化。

  • -eq 不區分大小寫的等價
  • 不區分大小寫的相等 -ieq
  • -ceq 區分大小寫的相等性

-ne 不相等

許多運算子都有一個相關的運算子,用於檢查相反的結果。 -ne 驗證這些值不相等。

if ( 5 -ne $value )
{
    # do something
}

使用此方法可確保只有在值未 5時,才會執行動作。 良好的使用案例,會在您嘗試啟動服務之前,先檢查服務是否處於執行中狀態。

變化:

  • 不相等(不區分大小寫) -ne
  • 不區分大小寫的不等於 -ine
  • -cne 區分大小寫不相等

這些是 -eq的反向變化。 當我列出其他運算符的變化時,我會將這些類型分組在一起。

-gt -ge -lt -le 大於或等於小於

檢查值是否大於或小於另一個值時,會使用這些運算符。 -gt -ge -lt -le 代表 GreaterThan、GreaterThanOrEqual、LessThan 和 LessThanOrEqual。

if ( $value -gt 5 )
{
    # do something
}

變化:

  • -gt 大於
  • -igt 大於不區分大小寫
  • -cgt 大於區分大小寫
  • -ge 大於或等於
  • -ige 大於或等於(不區分大小寫)
  • -cge 區分大小寫,大於或等於
  • -lt 小於
  • -ilt 小於,不區分大小寫
  • -clt 小於,區分大小寫
  • -le 小於或等於
  • -ile 小於或等於、不區分大小寫
  • -cle 小於或等於區分大小寫

我不知道為什麼您會針對這些運算符使用區分大小寫和不區分大小寫的選項。

-like 通配符匹配

PowerShell 有自己的通配符型模式比對語法,您可以搭配 -like 運算符使用。 這些通配符模式相當基本。

  • ? 符合任何單一字元
  • * 匹配任意數目的字元
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
    # do something
}

請務必指出模式符合整個字串。 如果您需要匹配字串中間的內容,需將 * 放在字串的兩端。

$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
    # do something
}

變化:

  • -like 不區分大小寫的通配符
  • -ilike 不區分大小寫的通配符
  • -clike 區分大小寫的通配符
  • -notlike 不分大小寫的通配符未匹配
  • -inotlike 不區分大小寫的通配符未匹配
  • -cnotlike 區分大小寫的通配符無法匹配

-match 正則表達式

-match 運算子可讓您檢查字串是否符合基於正則表達式的比對。 當通配符模式不夠彈性時,請使用此選項。

$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
    # do something
}

根據預設,regex 模式會比對字串中的任何位置。 因此,您可以指定您想要比對的子字串,如下所示:

$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
    # do something
}

Regex 是自己的複雜語言,值得研究。 我在另一篇文章中更深入討論 -match,以及使用正則表達式 的多種方式。

變化:

  • -match 不區分大小寫的正則表達式
  • -imatch 不區分大小寫的正則表達式
  • -cmatch 區分大小寫的正則表達式
  • -notmatch 不分大小寫的正則表達式不相符
  • -inotmatch 不區分大小寫的正則表達式不相符
  • -cnotmatch 區分大小寫的正則表達式不匹配

類型為-

您可以使用 -is 運算子來檢查值的型別。

if ( $value -is [string] )
{
    # do something
}

如果您正在處理類別或接受管線上的各種物件,您可以使用這。 您可以將服務或服務名稱作為輸入。 然後檢查您是否有服務,如果您只有名稱,則取得該服務。

if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
    $Service = Get-Service -Name $Service
}

變化:

  • -is 類型
  • -isnot 類型不是

集合運算子

當您使用前述運算子搭配單一值時,結果會是 $true$false。 使用集合時,處理方式稍有不同。 集合中的每個項目都會被評估,運算子會傳回所有評估結果為 $true的值。

PS> 1,2,3,4 -eq 3
3

這仍可在 if 語句中正常運作。 因此,運算子傳回一個值,然後整個運算式會是 $true

$array = 1..6
if ( $array -gt 3 )
{
    # do something
}

這裡有一個小陷阱隱藏在這裡的細節, 我需要指出。以這種方式使用 -ne 運算子時,很容易錯誤地向後查看邏輯。 使用 -ne 與集合搭配時,如果集合中的任何項目不符合您的值,則會傳回 $true

PS> 1,2,3 -ne 4
1
2
3

這看起來可能是一個聰明的技巧,但我們有運算子 -contains-in,以更有效率地處理這個。 -notcontains 符合你的預期。

-包含

-contains 運算子會檢查集合中是否包含您的值。 一旦找到匹配項目,就會傳回 $true

$array = 1..6
if ( $array -contains 3 )
{
    # do something
}

這是查看集合是否包含您值的慣用方式。 每次使用 Where-Object(或 -eq)遍歷整個清單,速度會明顯變慢。

變化:

  • -contains 不區分大小寫的比對
  • -icontains 不區分大小寫的比對
  • -ccontains 區分大小寫的匹配
  • 不相符的 -notcontains(不區分大小寫)
  • -inotcontains 不符(不區分大小寫)
  • 區分大小寫的 -cnotcontains 不相符

輸入

除了集合位於右側之外,-in 運算符與 -contains 運算符類似。

$array = 1..6
if ( 3 -in $array )
{
    # do something
}

變化:

  • 不區分大小寫的比對 -in
  • -iin 不區分大小寫的比對
  • -cin 大小寫敏感匹配
  • 不區分大小寫地未匹配 -notin
  • 不區分大小寫的 -inotin 未匹配
  • 區分大小寫不相符 -cnotin

邏輯運算子

邏輯運算子可用來反轉或合併其他表達式。

-不

-not 運算子會將表達式從 $false 翻轉為 $true,或從 $true 翻轉為 $false。 當 Test-Path$false時,我們想要執行動作的範例。

if ( -not ( Test-Path -Path $path ) )

我們談論的大部分運算子都有一種不用 -not 運算符的變化。 但有時它仍然有用。

! 操作員

您可以使用 ! 作為 -not的別名。

if ( -not $value ){}
if ( !$value ){}

您可能會發現使用像 C# 這樣的其他程式語言的人更常使用 !。 我比較喜歡全部打出來,因為我發現快速查看我的腳本時很難看清楚。

-和

您可以將表達式與 -and 運算子結合。 當您這樣做時,雙方都需要是 $true,這樣整個表示式才能是 $true

if ( ($age -gt 13) -and ($age -lt 55) )

在此範例中,$age 必須在左側是13或以上,右側小於55。 我新增了額外的括弧,以便在該範例中更清楚,但只要表達式很簡單,它們就會是選擇性的。 以下是沒有它們的相同範例。

if ( $age -gt 13 -and $age -lt 55 )

評估會從左至右進行。 如果第一個項目評估為 $false,它會提早結束,而且不會執行正確的比較。 當您需要在使用一個值之前,確認該值存在時,這很方便。 例如,如果您提供 $null 路徑,Test-Path 會擲回錯誤。

if ( $null -ne $path -and (Test-Path -Path $path) )

-或

-or 可讓您指定兩個表達式,並在其中一個表達式 $true時傳回 $true

if ( $age -le 13 -or $age -ge 55 )

就像 -and 運算符一樣,評估會從左到右進行。 如果第一個部分是 $true,那麼整個語句就是 $true,並且不會處理表達式的其餘部分。

也請記下這些運算符的語法運作方式。 您需要兩個不同的表達式。 我見過用戶嘗試執行類似 $value -eq 5 -or 6 的操作,而沒有意識到他們的錯誤。

-xor 獨佔或

這有點不尋常。 -xor 只允許表示式評估為 $true。 因此,如果兩個專案都 $false,或兩個專案都 $true,則整個表達式會 $false。 另一種看待這個問題的方法是,只有當表達式的結果不同時,該表達式才會是 $true

很少有人會使用這個邏輯運算符,我不能想出一個很好的範例,為什麼我會使用它。

位元運算子

位元運算子對值內的位元執行計算,並產生新的值作為結果。 教導 位元運算子 超出了本文討論的範圍,但以下是這些運算子的清單。

  • -band 二進位AND
  • -bor 二進位或運算
  • -bxor 二進位獨佔 OR
  • -bnot 二進位 NOT
  • 左移 -shl
  • -shr 右移

PowerShell 表達式

我們可以在條件語句內使用一般 PowerShell。

if ( Test-Path -Path $Path )

Test-Path 會在執行時傳回 $true$false。 這也適用於傳回其他值的命令。

if ( Get-Process Notepad* )

如果有傳回的進程,結果為 $true,如果沒有,則為 $false。 使用管線表達式或其他PowerShell語句非常有效,如下所示:

if ( Get-Process | where Name -EQ Notepad )

這些表達式可以彼此結合 -and-or 運算符,但您可能必須使用括號將它們分成子表達式。

if ( (Get-Process) -and (Get-Service) )

檢查$null

if 語句中,沒有任何結果或 $null 值會評估為 $false。 檢查 $null時,最佳做法是將 $null 放在左邊。

if ( $null -eq $value )

在 PowerShell 中處理 $null 值時,有相當多的細微差別。 如果您對深入潛水感興趣,我有一篇關於 您想要瞭解$null一切的文章。

條件內的變數指派

我差點忘了加上這個,幸好 普拉松卡魯南 V 提醒了我。

if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}

一般而言,當您將值指派給變數時,值不會傳遞至管線或控制台。 當您在子表達式中執行變數指派時,它會傳遞至管線。

PS> $first = 1
PS> ($second = 2)
2

查看 $first 指派沒有輸出,以及 $second 指派如何? 在 if 語句中完成指派時,它會執行如同上述 $second 指派。 以下是一個清楚的範例,說明如何使用它:

if ( $process = Get-Process Notepad* )
{
    $process | Stop-Process
}

如果 $process 被指派值,則語句會 $true$process 停止。

請注意不要將此與 -eq 混淆,因為這不是相等性檢查。 這是一個更不為人知的功能,大多數人不知道它是這樣運作的。

腳本區塊中的變數指派

您也可以使用 if 語句 scriptblock 將值指派給變數。

$discount = if ( $age -ge 55 )
{
    Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
    Get-ChildDiscount
}
else
{
    0.00
}

每個腳本區塊都會將命令的結果或值寫入為輸出。 我們可以將 if 語句的結果指派給 $discount 變數。 該範例可以同樣輕鬆地直接在每個腳本區塊中將這些值分配給 $discount 變數。 我不能說我經常在 if 語句中使用這個, 但我確實有一個範例, 我最近使用了這個。

替代執行路徑

if 語句不僅允許您在語句為 $true時指定動作,也允許您在語句為 $false時指定動作。 這就是 else 語句發揮作用的地方。

否則

使用時,else 語句一律是 if 語句的最後一個部分。

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."
}

在此範例中,我們會檢查 $path 以確定它是檔案。 如果找到檔案,我們會移動它。 如果沒有,我們會撰寫警告。 這種類型的分支邏輯非常常見。

巢狀條件

ifelse 語句會採用腳本區塊,因此我們可以將任何 PowerShell 命令放在其中,包括另一個 if 語句。 這可讓您使用更複雜的邏輯。

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."
    }
}

在此範例中,我們會先測試快樂路徑,然後對它採取動作。 如果失敗,我們會進行另一項檢查,並提供更詳細的資訊給使用者。

elseif

我們不限於單一條件式檢查。 我們可以將 ifelse 語句鏈結在一起,而不是使用 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."
}

執行會從上到下執行。 頂部的 if 語句會被先評估。 如果這是 $false,則會向下移至清單中的下一個 elseifelse。 如果其他人未傳回 $true,則最後一個 else 是要採取的默認動作。

開關

此時,我需要提及 switch 語句。 它提供了一種替代語法,用來對一個值進行多重比較。 使用 switch,您可以指定表示式,而且結果會與數個不同的值進行比較。 如果其中一個值相符,則會執行相符的程式代碼區塊。 請看一下此範例:

$itemType = 'Role'
switch ( $itemType )
{
    'Component'
    {
        'is a component'
    }
    'Role'
    {
        'is a role'
    }
    'Location'
    {
        'is a location'
    }
}

有三個可能的值可以比對 $itemType。 在此情況下,它會與 Role相符。 我使用了簡單的範例,只是為了讓您接觸 switch 運算符。 我更深入瞭解 您在另一篇文章中想知道的 switch 語句

數位內嵌

我有一個名為 Invoke-SnowSql 的函式,會啟動具有數個命令行自變數的可執行檔。 以下是該函式的一個片段,其中我建構參數陣列。

$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
    }
)

$Debug$Path 變數是使用者所提供的函式參數。 我在初始化陣列時內部評估它們。 如果 $Debug 為 true,則這些值會落入正確的位置 $snowSqlParam$Path 變數也是如此。

簡化複雜的作業

不可避免地,您會遇到需要檢查過多比較的情況,導致您的 if 語句在螢幕右側滾動得很遠。

$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
}

他們可能很難閱讀,這會使你更容易犯下錯誤。 關於那件事,我們可以做一些事情。

線條接續

PowerShell 中有一些運算子可讓您將命令包裝至下一行。 如果您想要將表達式分成多行,邏輯運算子 -and-or 是很好的運算符。

if ($null -ne $user -and
    $user.Department -eq 'Finance' -and
    $user.Title -match 'Senior' -and
    $user.HomeDrive -notlike '\\server\*'
)
{
    # Do Something
}

那裡還有很多事情,但把每一塊放在自己的線上都有很大的不同。 當我取得兩個以上的比較時,或者如果我必須捲動到右側,才能讀取任何邏輯,我通常會使用此方式。

預先計算結果

我們可以從 if 語句中取出該語句,並只檢查結果。

$needsSecureHomeDrive = $null -ne $user -and
    $user.Department -eq 'Finance' -and
    $user.Title -match 'Senior' -and
    $user.HomeDrive -notlike '\\server\*'

if ( $needsSecureHomeDrive )
{
    # Do Something
}

這比上一個範例更簡潔。 您也有機會使用變數名稱來說明您真正檢查的內容。 這也是自我記錄程式碼的範例,可儲存不必要的批注。

多個 if 陳述式

我們可以將此分成多個語句,並逐一檢查。 在此情況下,我們會使用旗標或追蹤變數來合併結果。


$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
}

我確實必須反轉邏輯,讓旗標邏輯正常運作。 每個評估都是個別的 if 陳述。 其優點是,當您進行偵錯時,您可以準確地了解邏輯正在執行的操作。 我能夠同時增加更高的冗長性。

明顯的缺點是,撰寫的程式代碼要多得多。 程式代碼會比較複雜,因為它需要一行邏輯,並將其分解成 25 行或更多行。

使用函式

我們也可以將所有驗證邏輯移至函式。 看看這看起來有多乾淨。

if ( Test-SecureDriveConfiguration -ADUser $user )
{
    # do something
}

您仍然需要建立 函式來執行驗證,但它可讓此程式代碼更容易使用。 這可讓此程式代碼更容易測試。 在您的測試中,您可以模擬對 Test-ADDriveConfiguration 的呼叫,而且您只需要此函式的兩個測試。 其中一個會傳回 $true,另一個傳回 $false。 測試另一個函式比較簡單,因為它太小了。

該函式的主體可能仍然是我們開始使用的單行程式碼,或是我們在最後一節中使用的詳細展開的邏輯。 這適用於這兩種案例,並可讓您稍後輕鬆變更該實作。

錯誤處理

if 陳述式的一個重要用法是先檢查錯誤條件,以防止錯誤發生。 一個很好的範例是先檢查資料夾是否已經存在,再嘗試建立資料夾。

if ( -not (Test-Path -Path $folder) )
{
    New-Item -Type Directory -Path $folder
}

我想說,如果你預期會發生例外狀況,那麼這不是一個例外狀況。 因此,請檢查您的數值,並在條件允許時進行驗證。

如果您想要深入了解實際的例外狀況處理,我有一篇關於 您想要瞭解例外狀況的文章

最後一個字

if 語句雖然簡單,卻是 PowerShell 的基本組成部分。 在幾乎每一個您撰寫的腳本中,您都會發現自己會多次使用此方式。 希望你比以前有更深刻的理解。