相关文章推荐
着急的回锅肉  ·  無料会員登録·  1 年前    · 

與許多其他語言一樣,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 ,而且會在 scriptblock 內執行 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 以及 許多使用 RegEx 的方式。

  • -match 不區分大小寫的 RegEx
  • -imatch 不區分大小寫的 RegEx
  • -cmatch 區分大小寫的 RegEx
  • -notmatch 不區分大小寫的 RegEx 不相符
  • -inotmatch 不區分大小寫的 RegEx 不相符
  • -cnotmatch 區分大小寫的 RegEx 不相符
  • -is 的類型

    您可以使用 運算子來檢查值的型 -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
    

    這仍可在 語句中 if 正常運作。 因此,運算子會傳回值,則整個語句為 $true

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

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

    PS> 1,2,3 -ne 4
    

    這可能看起來像一個聰明的技巧,但我們有運算子 -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 。 以下是我們想要在 是 $falseTest-Path 執行動作的範例。

    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 ,它會提早結束,而且不會執行正確的比較。 當您需要使用該值之前,請務必確定值存在時,這很方便。 例如, Test-Path 如果您提供 $null 路徑,則會擲回錯誤。

    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 binary AND
  • -bor binary OR
  • -bxor 二進位獨佔 OR
  • -bnot binary NOT
  • -shl 左移位
  • -shr 向右移位
  • PowerShell 運算式

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

    if ( Test-Path -Path $Path )
    

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

    if ( Get-Process Notepad* )
    

    它會評估為 是否有傳回的進程, $false 如果沒有, 則評估 $true 為 。 使用管線運算式或其他 PowerShell 語句非常有效,如下所示:

    if ( Get-Process | Where Name -eq Notepad )
    

    這些運算式可以與 和 -or 運算子相互 -and 結合,但您可能必須使用括弧將它們分成子運算式。

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

    檢查$null

    在 語句中沒有結果或 $null 值評估為 $falseif 。 特別 $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)
    

    查看指派沒有輸出和 $second 指派的方式 $first 為何? 在 語句中 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
    

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

    替代執行路徑

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

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

    if ( Test-Path -Path $Path -PathType Leaf )
        Move-Item -Path $Path -Destination $archivePath
        Write-Warning "$path doesn't exist or isn't a file."
    

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

    巢狀 if

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

    if ( Test-Path -Path $Path -PathType Leaf )
        Move-Item -Path $Path -Destination $archivePath
        if ( Test-Path -Path $Path )
            Write-Warning "A file was required but a directory was found instead."
            Write-Warning "$path could not be found."
    

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

    elseif

    我們不限於單一條件式檢查。 我們可以將和 else 語句鏈結if在一起,而不是使用 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."
        Write-Warning "$path could not be found."
    

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

    switch

    此時,我需要提及 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
            '--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 中有一些運算子可讓您將命令包裝至下一行。 如果您想要將表達式分成多行,則邏輯運算符和 -or 是要使用的良好運算符-and

    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 的基本部分。 在幾乎每一個您撰寫的腳本中,您都會發現自己會多次使用此方式。 我希望你有一個比你以前更好的理解。