2016-07-26 21 views
13

Niedawno zaktualizowaliśmy wersję Powershell na naszych serwerach budowania od 4.0 do 5.0. Ta zmiana spowodowała, że ​​jeden z naszych skryptów kompilacji zaczął działać niepoprawnie.Jaka zmiana w Powershell 5 zmienia znaczenie nawiasów klamrowych bloku

Kod służy do określenia, które przewodniki użytkownika powinny być dołączone do naszego produktu. Kod przetwarza listę węzłów XML, które opisują wszystkie dostępne dokumenty z wersją i kulturą. Grupujemy według tytułu dokumentu i kultury, a następnie wybieramy najbardziej odpowiednią wersję.

$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 
    } 

W pakiecie Powershell 4 ten kod poprawnie sortuje dokumenty według tytułu i kultury, a następnie wybiera najbardziej odpowiednią wersję. W pakiecie Powershell 5 ten kod grupuje wszystkie dokumenty na jednej liście, a następnie wybiera najbardziej odpowiednią wersję z tej listy. Biorąc pod uwagę, że mamy dokumenty w wielu językach, oznacza to, że będzie obecny tylko język o najbardziej odpowiedniej wersji.

Problem został rozwiązany przez zmianę

$documents = $documents | Group-Object { $_.ows_Title, $_.ows_Localisation } | 

do

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

teraz rozumiem, że pierwsza składnia nie jest technicznie poprawne zgodnie z dokumentacją ponieważ Group-Object spodziewa tablicę nazw własności zgrupować, jednak w Powershell 4 kod zwrócił pożądane wyniki.

Teraz powstaje pytanie, co zmieniło się w PowerShell 5, że oryginalny kod pracował w PowerShell 4, ale nie powiodło się w PowerShell 5.

+0

Widzę to samo, jeśli testuję pliki grupujące ('ls | grupa {$ _. Length, $ _. Nazwa} '). Nie wygląda jak dokumenty dla [PSv4] (https://technet.microsoft.com/library/hh849907.aspx) i [PSv5] (https://technet.microsoft.com/en-gb/library/hh849907 (v = wps.630) .aspx) odzwierciedlają wszelkie zmiany. Spójrz na przykłady 3 i 6 - to jest poprawna składnia dla obliczonej właściwości. Mam wrażenie, że w wersji 4 skrypt blokuje tablicę, każdy element jest konwertowany na ciąg znaków, a następnie działa jak przekazywanie tablicy właściwości, aw PSv5 wynik bloku skryptowego jest konwertowany na jeden ciąg i używany jako jedna nazwa właściwości. – TessellatingHeckler

+0

Nie wierzę, że użycie {...} z obiektami grupowymi było technicznie poprawne ... może wyglądało to na foreach-object i where = object? O ile nie ma dokumentacji, którą ktoś ma z Grupą-Obiekt {...} –

+0

Po drugie, wydaje się, że oświadczenie powinno być "$ dokumenty = $ dokumenty | Group-Object -Property {$ _. Ows_Title, $ _. Ows_Localisation} 'cały czas ... –

Odpowiedz

6

To nie wygląda jak składni polecenia cmdlet Group-Object została zmieniona, ponieważ Poniżej przedstawiono tę samą definicję (wraz z DLL w którym metoda jest zdefiniowana) dla obu wersji:

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>] 

Ale jak PetSerAL wspomniano w komentarzu wygląda na to 5,0 DLL obsługuje macierze inaczej niż 4,0. Na przykład:

$a=[PSCustomObject]@{[email protected](1,2)} #Object with an array for the value of the item property 
$b=[PSCustomObject]@{[email protected](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]@{[email protected]{key='value'}} #Similar but this time the item value is a hashtable 
$d=[PSCustomObject]@{[email protected]{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}} 

Należy zauważyć, że w wersji 4 wartości w tablicy były traktowane jako oddzielne grupy, ale tablice skrótów są traktowane jako grupy równe. Oznacza to, że tablice mają głębokie porównanie, ale hashtables były płytkim porównaniem (wszystkie hashtables są traktowane jako równoważne).

Teraz w wersji 5 tablice są traktowane jako równoważne, co oznacza, że ​​są płytkie, podobnie jak w przypadku tablic asynchronicznych.

Jeśli chcesz zobaczyć pełne szczegóły, będziesz musiał użyć ilspy lub .Net Reflector, aby zdemontować bibliotekę DLL i porównać metodę DoGrouping klasy Microsoft.PowerShell.Commands.GroupObjectCommand. Trudno powiedzieć, czy jest to błąd, czy nie, ale zdecydowanie jest to przełomowa zmiana dla cmdletu grupy-obiektu.

Im więcej gram, tym bardziej uważam, że nowy kod jest poprawny (poza wyświetlaną nazwą powinien być po prostu System.Object) i wystąpił błąd w starym kodzie. Wygląda na to, że v4 robiło pewne porównanie oparte na łańcuchach, ponieważ nawet dwie różne macierze z tymi samymi elementami byłyby zgrupowane razem, chociaż $a.Equals([PSCustomObject]@{[email protected](1,2)}) jest zawsze fałszywe (ich wyniki metody GetHashCode nie pasują do siebie). Jedynym sposobem, w jaki mogłem uzyskać 5.0 do grupowania podobnych tablic, było użycie group -Property {$_.item -join ','}, które pasowało do wyjścia 4.0, z tą różnicą, że było to 1,2 zamiast {1, 2}.Również jeśli chcesz zgrupować za pomocą klucza elementu hashtable, użyjesz group -Property {$_.item.somekey} (zakładając, że wszystkie mają wartość dla somekey)

Powiązane problemy