最近依舊是寫 PowerShell 多過 C# (雖然在 PowerShell 裡卯起來用 .NET 元件),開始進入見山不是山的階段,程式寫得更多之後,踩到一些奇怪特性,會覺得自己並不懂 PowerShell,哈!

下面這個範例是一個讓我吶喊「花惹發」的行為。我在函式裡建立 System.Data.DataTable 回傳給呼叫端,為了對照在內部先分別檢查 .Rows.Count 及 .Columns.Count,預期數值應為 2 跟 3。呼叫端接收存入參數,再檢查一次 .Rows.Count 及 .Columns.Count,大家覺得數字會是多少?

function CreateDemoDataTable() {
    $dt = New-Object System.Data.DataTable
    $dt.Columns.Add("Id", [int]) | Out-Null
    $dt.Columns.Add("Name", [string]) | Out-Null
    $dt.Columns.Add("RegDate", [datetime]) | Out-Null
    $dt.Rows.Add(1, "Jeffrey", (Get-Date)) | Out-Null
    $dt.Rows.Add(2, "Jack", (Get-Date)) | Out-Null
    Write-Host "Internal Check:" -ForegroundColor Yellow
    Write-Host "  Rows.Count = $($dt.Rows.Count)"    
    Write-Host "  Columns.Count = $($dt.Columns.Count)"
    return $dt
}

$dt = CreateDemoDataTable
Write-Host "External Check:" -ForegroundColor Yellow
Write-Host "  Rows.Count = $($dt.Rows.Count)"
Write-Host "  Columns.Count = $($dt.Columns.Count)"
Write-Host "`$dt.WTF.Count = $($dt.WTF.Count)" -ForegroundColor DarkRed
Write-Host "`$dt.GetType = $($dt.GetType())"

答案揭曉, .Rows.Count 及 .Columns.Count 都是 2,搞什麼鬼,乾脆怒查一個 $dt.WTF.Count,這總會出現錯誤吧?登楞! 沒出錯,也傳回 2。最後檢查 $dt.GetType(),發現它不是 DataTable,而是 System.Object[]。

爬了文大概才知道是怎麼一回事。

依據官方文件

When you return a collection from your script block or function, PowerShell automatically unrolls the members and passes them one at a time through the pipeline. This is due to PowerShell's one-at-a-time processing.
在程式區塊或函式傳回集合物件時,PowerShell 會自動展開集合,以便一次一個元素送進 Pipeline

要避免集合被展開,有兩種寫法:

  1. return (, $collectionObject) 或直接 return ,$collectionObject
  2. return Write-Output -NoEnumerate $colletionObject 或直接 Write-Output $collectionObject

在 return $dt 前加上逗號,就能順利傳回 DataTable,.Columns.Count 及 .Rows.Count 也正常了。

但 $dt.WTF.Count = 2 是怎麼回事?

我做了一個實驗,發現在 $dt 後方接上不存在的屬性名稱,會得到 object[],在不知名屬性名稱後方加上 .Count 會得到 Rows 筆數,但不知名屬性 -eq $null 卻又相等! 這...

猜想是背後某個轉型原則始然,但爬了好一陣子文章也沒找到解答。這個謎團,就留待等高手先進路過再幫忙指點迷津了。

[2024-09-17 中秋節更新]此行為與應 Member Access Enumeration 有關,參考

Introduce to the unrolling behavior when returning collection type in PowerShell.


Comments

# by 艾力萊茵

一般只用Powershell來做安裝script 和 在citrix 上做configuration 長智識了,黑大謝謝你的分享 另外,在下在自家的部落格 link了你的Blog 希望黑大不會介意

Post a comment