與許多其他語言一樣,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
。 以下是我們想要在 是 $false
時 Test-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
值評估為 $false
if
。 特別 $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
if
和 else
語句會採用腳本區塊,因此我們可以將任何 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
如果是 ,則會向下移至下一個elseif
或else
清單中的 。 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 的基本部分。 在幾乎每一個您撰寫的腳本中,您都會發現自己會多次使用此方式。 我希望你有一個比你以前更好的理解。