捐血一袋救人一命

2024年3月12日 星期二

Powershell 流程控制

If

If (<test1>)
    {<statement list 1>}
[elseif (<test2>)
    {<statement list 2>}]
[else
    {<statement list 3>}]

簡略寫法

<condition> ? <if-true> : <if-false>

Switch

基本語法

Switch ( <test-expression> ){ 
	<result1-to-be-matched> {<action>} 
	<result2-to-be-matched> {<action>}
	Default {<action>} # optional
}

特別參數及語法

Switch [-regex | -wildcard | -exact] [-casesensitive] ( <test-expression> ){
    "string1" | number1 | variable1 | { <value-scriptblock1> } { <action-scriptblock> }
    "string2" | number2 | variable2 | { <value-scriptblock2> } { <action-scriptblock> }
    Default { <action-scriptblock> } # optional
}

從檔案取得資料

Switch [-regex | -wildcard | -exact] [-casesensitive] -file filename {
    "string" | number | variable | { <value-scriptblock> } { <action-scriptblock> }
    default { <action-scriptblock> }  # optional
}

說明:

參數 說明
-regex <test-expression> 的符合條件是正規式;如果 <test-expression> 變數型態不是字串,此參數會無效
-wildcard 使用萬用字元去比對,這裡不分大小寫;如果 <test-expression> 變數型態不是字串,此參數會無效
-exact 必須完全符合字串大小寫;如果 <test-expression> 變數型態不是字串,此參數會無效
-casesensitive 區分大小寫;如果 <test-expression> 變數型態不是字串,此參數會無效
-file 從文字型檔案讀取每一行來比較,這裡不分大小寫
  • 範例1:使用 Script Blocks 來媒合,Switch 會執行所有吻合條件的部分

    $a = 1,2,3
    $Variable = 2
    Switch($Variable){
    	{ $_ -in $a }{
            "Variable is in Array"
    	}
    	{ $_ -match "[0-9]" }{
            "Variable is matched regexp"
    	}
    	{ $_ -lt 10 -and $_ -gt 1 }{
            "Variable is bigger than 1, and smaller than 10"
    	}
    	Default {
            "Otherwise"
    	}
    }
    

    enter image description here

  • 範例二:要中斷Switch繼續媒合所有 Script Block,要使用 Break

    $a = 1,2,3
    $Variable = 2
    Switch($Variable){
    	{ $_ -in $a }{
            "Variable is in Array"
            break
    	}
    	{ $_ -match "[0-9]" }{
            "Variable is matched regexp"
            break
    	}
    	{ $_ -lt 10 -and $_ -gt 1 }{
            "Variable is bigger than 1, and smaller than 10"
            break
    	}
    	Default {
            "Otherwise"
    	}
    }
    

    enter image description here

  • 範例三:

    $Variable = 42
    Switch($Variable){
    	{ 0..20 -contains $_ }{
            "Variable is between 0~20"
    	}
    	{ 21..40 -contains $_ }{
            "Variable is between 21~40"
    	}
    	{ 41..60 -contains $_ }{
            "Variable is between 41~60"
    	}
    	{ 61..80 -contains $_ }{
            "Variable is between 61~80"
    	}
    	{ 81..100 -contains $_ }{
            "Variable is between 81~100"
    	}
    	Default{
            "Variable is not in 0~100"
    	}
    }
    

    enter image description here

  • 範例四:媒合條件為正規式

    $month = (Get-Date).Month
    $month
    switch -regex ([string]$month) {
     "^[1-3]$" {"Q1"}
     "^[4-6]$" {"Q2"}
     "^[7-9]$" {"Q3"}
     "^1[0-2]$" {"Q4"}
    }
    

    enter image description here

Comparison Operators 比較運算說明

比較 說明
-and 進行邏輯運算 AND
-or 進行邏輯運算 OR
-not 進行邏輯運算 NOT
-xor 進行邏輯運算 XOR,而非 bit XOR運算
-is 判斷變數型態是否與比對的型態相同
-isnot 判斷變數型態是否與比對的型態不相同
-like 字串匹配通配符模式,忽略大小寫
-ilike 字串匹配通配符模式,忽略大小寫
-clike 字串匹配通配符模式,比較大小寫
-notlike 字串與通配符模式不匹配,忽略大小寫
-inotlike 字串與通配符模式不匹配,忽略大小寫
-cnotlike 字串與通配符模式不匹配,比較大小寫
-match 字串與正規表示式模式匹配,忽略大小寫
-imatch 字串與正規表示式模式匹配,忽略大小寫
-cmatch 字串與正規表示式模式匹配,比較大小寫
-notmatch 字串與正規表示式模式不匹配,忽略大小寫
-inotmatch 字串與正規表示式模式不匹配,忽略大小寫
-cnotmatch 字串與正規表示式模式不匹配,比較大小寫
-in 變數值存在於集合之中,忽略大小寫
-iin 變數值存在於集合之中,忽略大小寫
-cin 變數值存在於集合之中,比較大小寫
-notin 變數值不存在於集合之中,忽略大小寫
-inotin 變數值不存在於集合之中,忽略大小寫
-cnotin 變數值不存在於集合之中,比較大小寫
-contains 集合中包含變數值,忽略大小寫
-icontains 集合中包含變數值,忽略大小寫
-ccontains 集合中包含變數值,比較大小寫
-eq Equal 等於(忽略大小寫)
-ieq Equal 等於,忽略大小寫
-ceq Equal 等於,比較大小寫
-ne Not Equal 不等於,忽略大小寫
-ine Not Equal 不等於,忽略大小寫
-cne Not Equal 不等於,比較大小寫
-gt Great Than 大於,忽略大小寫
-igt Great Than 大於,忽略大小寫
-cgt Great Than 大於,比較大小寫
-ge Great Equal 大於等於,忽略大小寫
-ige Great Equal 大於等於,忽略大小寫
-cge Great Equal 大於等於,比較大小寫
-lt Less Than 小於,忽略大小寫
-ilt Less Than 小於,忽略大小寫
-clt Less Than 小於,比較大小寫
-le Less Equal 小於等於,忽略大小寫
-ile Less Equal 小於等於,忽略大小寫
-cle Less Equal 小於等於,比較大小寫
  • 仔細觀察可以發現,只要加上 i 就是不區分大小寫;加上 c 就是區分大小寫

  • 從範例來理解 -in & -contains

    $a = 5
    
    1..3 -contains $a
    1..10 -contains $a
    
    $a -in 1..3
    $a -in 1..10
    
  • 從範例來了解 -is & -isnot

    $a = 5
    $b = "5"
    $a.GetType()
    $b.GetType()
    $a -is [int]
    $a -isnot $b.GetType()
    

    enter image description here

  • 需要特別注意的特例

$a = 1, 2, $null, 4, $null, 6
$a.GetType()
$a -ne $null

$null -ne $a

enter image description here

"abc" -eq "abc"         # Output: True
"abc" -eq "abc", "def"  # Output: False
"abc" -ne "def"         # Output: True
"abc" -ne "abc"         # Output: False
"abc" -ne "abc", "def"  # Output: True

"abc", "def" -eq "abc"  # Output: abc
"abc", "def" -ne "abc"  # Output: def

enter image description here
說明:當集合在運算子左邊時,其結果會是過濾變數值

  • 再來一個自訂類別的比較
class MyFileInfoSet {
    [String]$File
    [Int64]$Size
}
$a = [MyFileInfoSet]@{File = "C:\Windows\explorer.exe"; Size = 4651032}
$b = [MyFileInfoSet]@{File = "C:\Windows\explorer.exe"; Size = 4651032}
$a -eq $b

上面程式碼雖然透過自訂類別,設定了兩個變數,兩個變數的屬性值雖然都一樣,但這兩個變數是不同的物件, -eq 會認為這不是同一個物件。

所以要讓 -eq 能判斷物件"值" 是否一致,要改用以下程式,讓程式知道使用 -eq 比較時,是要比較兩個變數的值

class MyFileInfoSet : System.IEquatable[Object]{ 
	[String]$File 
	[Int64]$Size 
	[bool] Equals([Object] $obj){ 
		return ($this.File -eq  $obj.File) -and ($this.Size -eq  $obj.Size) 
	}
}
$c = [MyFileInfoSet]@{File = "C:\Windows\explorer.exe"; Size = 4651032}
$d = [MyFileInfoSet]@{File = "C:\Windows\explorer.exe"; Size = 4651032}
$c -eq  $d

我們再來看看兩個類別宣告出來的物件變數的方法有甚麼不同

class MyFileInfoSet {
    [String]$File
    [Int64]$Size
}
$a = [MyFileInfoSet]@{File = "C:\Windows\explorer.exe"; Size = 4651032}
$a | Get-Member

class MyFileInfoSet2 : System.IEquatable[Object]{ 
	[String]$File 
	[Int64]$Size 
	[bool] Equals([Object] $obj){ 
		return ($this.File -eq  $obj.File) -and ($this.Size -eq  $obj.Size) 
	}
}

$c = [MyFileInfoSet2]@{File = "C:\Windows\explorer.exe"; Size = 4651032}
$c | Get-Member

($c | Get-Member) | Where-Object { $_.Name -eq "Equals" } | Select-Object -Property Definition

enter image description here

Break、Continue、Return、Exit

Break 中斷迴圈或是 Switch,或是 Trap

  • 範例一:

    $i=0
    $varB = 10,20,30,40
    foreach ($val in $varB) {
      if ($val -eq 30) {
        break
      }
      $i++
    }
    Write-Host "30 was found in array index $i"
    
  • 範例二:中斷指定的迴圈(這是很差的程式習慣,請盡量不要使用)

    Remove-Variable * -ErrorAction SilentlyContinue
    $b = 3
    :red while ($true) {
      :yellow while ($true) {
        while ($true) {
          if ($a) {break}
          if ($b) {break yellow}
          if ($c) {break red}
        }
        Write-Host "中斷最內層迴圈"
      }
      Write-Host "中斷 :yellow 迴圈"
    }
    Write-Host "中斷 :red 迴圈"
    

如果有定義 $a,就會中斷最內層迴圈
如果有定義 $b,就會中斷 :yellow 迴圈
如果有定義 $c,就會中斷 :red 迴圈

  • 範例三:
    function test {
      trap [DivideByZeroException] {
        Write-Host 'divide by zero trapped'
        break
      }
    
      $i = 3
      'Before loop'
      while ($true) {
         "1 / $i = " + (1 / $i--)
      }
      'After loop'
    }
    test
    
    enter image description here
    當除數為0的例外被觸發時,印出 “divide by zero trapped”
    然後就終止函式
    如果沒有使用 Break,就會在發生錯誤之後,繼續執行印出 “After loop”

請不要在迴圈(不包含 ForEach-Object)、Switch、Trap 之外使用 Break!這會目前的 Runspace 被終止!

function test {
 "TEST"
 break
}

test
"After Test"

當程式執行 test 函式時,輸出 “TEST” 之後,整個程式就被 break 終止了!所以 “After Test” 永遠不會被執行到。

1..10 | ForEach-Object{
    If ($_ -eq 3){
        Break
    }
    $_
}

"迴圈之後,繼續執行"

在 ForEach-Object 內使用 Break,也會導致整個程式被終止!

ForEach ($v in 1..10){
    If ($v -eq 3){
        Break
    }
    $v
}

"迴圈之後,繼續執行"

但是在 ForEach ($variable in <collection>){} 迴圈卻不會中斷整個程式

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_continue?view=powershell-7.3

0 意見: