分享最近學到的 PowerShell 小技巧。

假設我有個接受多個參數的函式,有三種參數寫法。第一種是寫成 FuncName Arg1 Arg2 Arg3... 依序列出,中間以空白間隔(注意:不要加 ( ) 及 ,,參考:函式多參數寫法陷阱);第二種則是參數值前方加上名稱,如此可不依順序,如:FuncName -Arg3 Arg3 -Arg1 Arg1 -Arg2 Arg2;第三種則是混合,前幾個依序,後方選擇性參數以具名方式指定,例如:FuncName Arg1 -Arg3 Arg3

#函式接受三個參數組成特定格式字串
function PrintRecord(
    [string]$brand, [string]$model, [int]$year
) 
{
    Write-Host "[$brand] $model - $year"
}

# 函式多參數寫法 REF:
# 依序傳入
PrintRecord "Thinkpad" "X21" 2001
# 具名傳入
PrintRecord -brand "Thinkpad" -year 2001 -model "X21"
# 依序+具名混合
PrintRecord 2001 -brand "Thinkpad" -model "X21"

問題來了,在以上的例子,如果參數來自資料庫或 CSV,每一筆的 $brand、$model、$year 存在三元素陣列中,要怎麼傳給 PrintRecord?

不想花腦筋的話,在迴圈寫死 PrintRecord $ary[0] $ary[1] $ary[2] 就好,但,有沒有簡潔一點的做法?

學到一個新名詞 - Splatting,關鍵是將陣列或雜湊表變數 $varName 的「$」改成「@」,寫成 @varName。若變數為陣列,PowerShell 將展開陣列以第一個元素對映第一個參數、第二個元素對映第二個參數... 以此類推;若變數為雜湊表,則相當於轉成 -key1 value1 -key2 value2 -key3 value3... 以此類推。如以下範例:

$data = (
    ("Thinkpad","X21", 2001),
    ("VAIO","T13", 2012),
    ("Thinkpad","T470p", 2017)
)

# 標準寫法
$data | ForEach-Object {
    PrintRecord $_[0] $_[1] $_[2]
}

# 原本 $_ 為陣列,改寫為 @_ 相當於將陣列元素依序對映函式參數
$data | ForEach-Object { PrintRecord @_ }

# 用 Hashtable 對映參數名稱及參數值,可以不依順序傳入參數
$data | ForEach-Object {
    $p = @{
        model = $_[1];
        year = $_[2];
        brand = $_[0];
    }
    # $p 改寫成 @p 會依 Hashtable Key 對映參數名稱
    PrintRecord @p
}

最後來個 $ 與 @ 的小測驗,我讓 F 函式的 $p1 可以接受陣列也可以接受字串,藉以觀察 $ 與 @ 結果差異。大家先猜猜結果為何,再看答案。

function F($p1, $p2 = $null) {
    Write-Host "`$p1[$($p1.GetType())] = $p1, `$p2 = $p2"
}

$array = ("P1", "P2")
# $array 視為單一參數,對映 $p1
F $array
# @array 將陣列展開,分別對映 $p1, $p2
F @array

F "P1" "P2"
F ("P1","P2")
# 陣列宣告前方的 @ 是 Array Sub-Expression Operator
# 無 Splatting 效果
F @("P1", "P2")

應該不難吧?只有最後一行是陷阱,F @("P1", "P2") 並不會形成 Splatting,因為陣列前方的 @ 將被解讀成 Array Sub-Expression Operator,而非 Splatting 符號。

Exmample of how to use platting to pass array as multiple arguemnts to fucntion in PowerShell.


Comments

Be the first to post a comment

Post a comment