Подтвердить что ты не робот

Какое изменение в Powershell 5 изменяет значение блочных фигурных скобок

Недавно мы обновили версию 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.

4b9b3361

Ответ 1

Не похоже, что синтаксис командлета Group-Object был изменен, так как ниже показано то же определение (вместе с 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]@{[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}}

Обратите внимание, что в версии 4 значения массива обрабатывались как отдельные группы, но хэш-таблицы рассматриваются как равные группы. Это означает, что массивы имели глубокое сравнение, но hashtables были неглубоким сравнением (все хэш-таблицы рассматриваются как эквивалентные)

Теперь в версии 5 массивы рассматриваются как эквивалентные, что означает, что они являются неглубоким сравнением, аналогичным тому, как работали hashtables.

Если вы хотите просмотреть полную информацию, вам нужно будет использовать ilspy или .Net Reflector для демонстрации DLL и сравнить метод DoGrouping класса Microsoft.PowerShell.Commands.GroupObjectCommand. Трудно сказать, является ли это ошибкой или нет, но это определенно является изменением для командлета group-object.

Обновление: Чем больше я играю с этим, тем больше я думаю, что новый код верен (за исключением того, что отображаемое имя должно быть просто System.Object), и была ошибка в старом коде. Похоже, что v4 выполнял какое-то сравнение на основе строк, так как даже два разных массива с одинаковыми элементами были бы сгруппированы вместе, хотя $a.Equals([PSCustomObject]@{[email protected](1,2)}) всегда false (результаты метода GetHashCode не совпадают). Единственный способ, которым я мог получить 5.0 для группировки подобных массивов, - это использовать group -Property {$_.item -join ','}, который соответствовал выходному сигналу 4.0, за исключением того, что имя было тогда 1,2 вместо {1, 2}. Кроме того, если вы хотите сгруппировать, используя ключ элемента хэш-таблицы, вы должны использовать group -Property {$_.item.somekey} (при условии, что все они имеют значение для некоторой клавиши)