Powershell 5中的哪些变化会改变块大括号的含义

时间:2016-07-26 21:46:17

标签: powershell

我们最近在构建服务器上将Powershell版本从4.0更新到5.0。此更改导致我们的一个构建脚本以意外方式启动失败。

该代码用于确定应在我们的产品中包含哪些用户指南。代码处理xml节点列表,这些节点描述具有版本和文化的所有可用文档。我们按文档标题和文化分组,然后选择最合适的版本。

$documents = Get-ListItemsFromSharePoint
$documents = $documents |
    Where-Object { $productVersion.CompareTo([version]$_.ows_Product_x0020_Version) -ge 0 } |
    Where-Object { -not ($_.ows_EncodedAbsUrl.Contains('/Legacy/')) }

Write-Verbose -Message "Filtered to: $($documents.length) rows"

# Filter to the highest version for each unique title per language
$documents = $documents | Group-Object { $_.ows_Title, $_.ows_Localisation } |
    ForEach-Object {
        $_.Group | Sort-Object { [version]$_.ows_Product_x0020_Version } -Descending | Select-Object -First 1
    }

在Powershell 4中,此代码正确地按标题和文化对文档进行排序,然后选择最合适的版本。在Powershell 5中,此代码将所有文档分组到一个列表中,然后从该列表中选择最合适的版本。鉴于我们有多种语言的文档,这意味着只会出现具有最合适版本的语言。

通过更改

解决了这个问题
$documents = $documents | Group-Object { $_.ows_Title, $_.ows_Localisation } |

$documents = $documents | Group-Object ows_Title, ows_Localisation |

现在我明白第一种语法在技术上并不符合文档,因为Group-Object期望一组属性名称可以分组,但是在Powershell 4中,代码确实返回了所需的结果。

现在的问题是Powershell 5中的变化是原始代码在Powershell 4中有效但在Powershell 5中失败了。

1 个答案:

答案 0 :(得分:6)

看起来Group-Object cmdlet的语法没有改变,因为下面显示了两个版本的相同定义(以及定义方法的DLL):

gcm Group-Object | fl DLL,Definition


DLL        : C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Utility\v4.0_
             3.0.0.0__31bf3856ad364e35\Microsoft.PowerShell.Commands.Utility.dll
Definition :
             Group-Object [[-Property] <Object[]>] [-NoElement] [-AsHashTable] [-AsString]
             [-InputObject <psobject>] [-Culture <string>] [-CaseSensitive] [<CommonParameters>]

但正如PetSerAL在评论中提到的那样,5.0 DLL处理数组的方式与4.0不同。例如:

$a=[PSCustomObject]@{item=@(1,2)}   #Object with an array for the value of the item property
$b=[PSCustomObject]@{item=@(3,3)}   #Object with a different array for the value of the item property
$a.item.Equals($b.item)   #This deep compare is false, as the two objects are not equal
$a.item.GetType().Equals($b.item.GetType())   #This "shallow" compare is true because both are the array type.
$c=[PSCustomObject]@{item=@{key='value'}}   #Similar but this time the item value is a hashtable
$d=[PSCustomObject]@{item=@{anotherkey='anothervalue'}}   #again comparing the two items we expect the result to be false if deep compared but true if shallow compared
$e=[PSCustomObject]@{item=get-date} #another test using two datetimes (another common "reference" type)
$f=[PSCustomObject]@{item=[datetime]::MinValue}
$a,$b,$c,$d,$e,$f | group -Property item   #now we see what happens when using group-object

#Output in PowerShell 4.0
Count Name                      Group
----- ----                      -----
    1 {1, 2}                    {@{item=System.Object[]}}
    1 {3, 3}                    {@{item=System.Object[]}}
    2 {System.Collections.Di... {@{item=System.Collections.Hashtable}, @{item=System.Collections...
    1 8/5/2016 9:45:36 PM       {@{item=8/5/2016 9:45:36 PM}}
    1 1/1/0001 12:00:00 AM      {@{item=1/1/0001 12:00:00 AM}}

#Output in PowerShell 5.0
Count Name                      Group
----- ----                      -----
    2 {1, 2}                    {@{item=System.Object[]}, @{item=System.Object[]}}
    2 {System.Collections.Di... {@{item=System.Collections.Hashtable}, @{item=System.Collections...
    1 8/5/2016 9:45:40 PM       {@{item=8/5/2016 9:45:40 PM}}
    1 1/1/0001 12:00:00 AM      {@{item=1/1/0001 12:00:00 AM}}

请注意,在版本4中,数组值被视为单独的组,但哈希表被视为相等的组。这意味着数组有一个深度比较,但哈希表是一个浅比较(所有哈希表被视为等价)

现在在版本5中,数组被视为等效,这意味着它们是一个浅层比较,类似于哈希表的工作方式。

如果要查看完整的详细信息,则需要使用ilspy或.Net Reflector来反汇编DLL并比较Microsoft.PowerShell.Commands.GroupObjectCommand类的DoGrouping方法。很难说它是否是一个bug,但它绝对是group-object cmdlet的重大变化。

更新:我玩的越多,我认为新代码更正确(除了显示的名称应该只是System.Object)并且旧代码中存在错误。似乎v4正在进行某种基于字符串的比较,因为即使$a.Equals([PSCustomObject]@{item=@(1,2)})总是为假(它们的GetHashCode方法结果不匹配),即使两个具有相同元素的不同数组也会被组合在一起。我能够获得5.0来对类似数组进行分组的唯一方法是使用group -Property {$_.item -join ','},它匹配4.0输出,但名称是1,2而不是{1,2}。此外,如果您想使用哈希表项的键进行分组,您可以使用group -Property {$_.item.somekey}(假设它们都有某个键的值)