寫了很多方便自己用的 PowerShell Function 指令後,發現有很多指令功能其實差不多,只有少部分不一樣,想說要來重構他們,但又不希望影響到既有使用方式,也就是 Function 名稱不改變,可以怎麼處理呢?想說能不能使用動態建立 Function 的方式來做,沒想到…還真的可以!
情境
前情提要一下,這樣之後看這篇文章的時候,比較能進入狀況。
假設我有一批 PowerShell Function 長得像這樣:
1 | function Func1($Description) { Write-Output "Result1 - $Description" } |
這三個 Function 動作長得很像,只有 Function 名稱和輸出的結果有些不同,如何在不影響其他地方的使用方式下,動態建立這些 Function 呢?
動態建立 Function
要動態建立 Function 比我想像中的簡單一些,先看最終用於動態建立 Function 的 Function 程式碼:
1 | function Add-DynamicFunction { |
上面我使用 Param 的方式接收兩個參數,分別會是 Function 名稱,以及執行 Function 時的動作。
然後用 Set-Variable 建立變數的 Cmdlet 將要建立的 Function 用文字的方式組合並設定給 Func 變數,接著執行 Invoke-Expression $Func 即將組合好的文字 Function 拿去給 PowerShell 執行環境執行。
這裡有幾個注意事項:
- 傳進去的 Function 名稱和動作都是用純文字表示,並且是必要的參數(所以設定
Mandatory = $true) - 組合的 Function 前面加上
global:表示是全域使用的 Function,否則之後會找不到此建立的 Function
有了動態建立 Function 的 Function 之後,就可以使用如下的方式,來動態建立 Function:
1 | # 動態建立 Function |
批量動態建立
有了基礎之後,就要來大量建立了 😀
在 PowerShell 中,有很多種建立物件的方式,我個人偏好使用 [PSCustomObject] 搭配 HashTable 來建立,這是最快速、畫面最清爽的建立方式。
然後只要把他們用 @() 括起來,就可以建立出陣列裡面包含多個物件的資料格式:
1 | $list = @( |
這時候你可以用清單變數自帶的 ForEach 方法、ForEach-Object Cmdlet、或用 foreach 語法來遍巡 $list 清單變數,但我建議使用第三種 foreach 語法,因為前面兩者通常會使用 $_ 來取得當前資料,而 $_ 是參考內部的 Scope,比較容易出現不如預期的狀況,相對的第三種 foreach 語法則比較不容易有問題。
如果還是偏好使用前兩個做法,可以先建立一個變數來接收
$_資料,例如$list = $_,這樣也可以避免 Scope 的問題。
1 | $list.ForEach({ Write-Output $_ }) |
OK!執行完 ForEach 之後,就完成了動態建立 Function 囉,這種靠資料驅動建立 Function 的感覺挺不錯的 😆
參考資料: