PowerShell Group-Object больше не разбивает объекты на коллекции фиксированного размера.

У меня было это в сценарии, который, как мне кажется, работал хорошо, но, похоже, перестал работать:

      $testList = @("object 1","object 2","object 3","object 4","object 5")
$counter = 0
$maxSize = 2
$groupedList = $testList | Group-Object { [math]::Floor($counter++ / $maxSize) }
$groupedList
$groupedList | Measure-Object

Раньше Measure-Object дал бы мне количество 3, но теперь я получаю количество 1.

Почему это больше не работает? Целое число счетчика больше не увеличивается в команде Group-Object?

1 ответ

Я не думаю, что ваша команда когда-либо работала должным образом (подробности см. В нижнем разделе).

Заменять:

      { [math]::Floor($counter++ / $maxSize) }

с:

      { [math]::Floor((Get-Variable -Scope 1 counter).Value++ / $maxSize) }

чтобы гарантировать, что это переменная вызывающего объекта, которая обновляется при вызовах блока скрипта.

Примечание:

  • $script:counter++тоже работает, но только если область вызова является областью верхнего уровня скрипта.

  • Get-Variable -Scope 1 counterявно нацелен на определение родительской области , которое является вызывающим; для краткости можно опустить -Scope 1, потому что по умолчанию возвращается переменная в ближайшей наследственной области.

  • Сантьяго Скуарзон предлагает другой вариант:

    • Вы можете определить свою переменную как объект ссылочного типа со свойством, а не как целое число, в простейшей форме как хеш -таблицу :

                $counter = @{ Value = 0 }
      
    • Благодаря тому, что теперь он содержит ссылку на объект, дочерняя область в блоке скрипта видит тот же самый объект при запросе значения переменной родительской области и может обновлять его. .Valueимущество:

                { [math]::Floor($counter.Value++ / $maxSize) }
      

Проблема в том, что блок сценария передается (позиционно) подразумеваемому -Propertyпараметрвыполняется в дочерней области, поэтому при каждом вызове неявно создается блочная локальная переменная:

  • То есть, $counter++не действует на существующую переменную в родительской области; он неявно создает локальную копию переменной (с текущим значением вызывающего объекта) и применяется к ней .

    • Это, возможно, удивительное поведение — локальная переменная неявно создается при (якобы) назначении переменной, которая существует в наследственной области — подробно обсуждается в этом ответе .

    • В связи с этим может показаться удивительным, что такой блок скрипта (который в целом включает в себя вычисляемые свойства) выполняется в дочерней области, а не непосредственно в вызывающей области для начала — см. GitHub issue #7157 .

  • Поскольку ваш ++является постинкрементом, это исходное значение (скопированное из переменной родительской области), которое действует как операнд LHS для операции деления /.

  • При выходе из блока скрипта локальный $counterпеременная выходит за пределы области видимости, и следующий вызов начинается с нуля.

Таким образом, фактически возвращаемое значение блока скрипта является фиксированным. 0, так что вы получите только одну группу.


Альтернативы использованию для «дробления» :

Ваше использование Group-Objectдля достижения «фрагментации» (объединения, разбиения, т. е. разделения ввода на массивы фиксированного размера) элегантно, но в конечном счете неэффективно, и это не потоковое решение, потому что весь ввод должен быть собран заранее (ни один из аспектов может не иметь значения в заданная ситуация).

  • Было бы здорово, если быSelect-Objectнапрямую поддерживает такую ​​функцию, которую предлагает GitHub issue #8270 , в виде -ReadCountпараметр:

            # WISHFUL THINKING
    PS> 1..5 | Select-Object -ReadCount 2 | ForEach-Object { "$_" }
    1 2
    3 4
    5
    
  • Этот ответ предоставляет пользовательскую функцию, Select-Chunk, который реализует этот функционал:

            # Assumes function Select-Chunk from the linked answer is defined.
    PS> 1..5 | Select-Chunk -ReadCount 2 | ForEach-Object { "$_" }
    1 2
    3 4
    5
    
Другие вопросы по тегам