PowerShell: как подсчитать коллекцию каналов? - PullRequest
1 голос
/ 26 октября 2019

Предположим, у меня есть процесс, который генерирует коллекцию объектов. Для очень простого примера рассмотрим $(1 | get-member). Я могу получить количество сгенерированных объектов:

PS C:\WINDOWS\system32> $(1 | get-member).count
21

или я могу что-то сделать с этими объектами.

PS C:\WINDOWS\system32> $(1 | get-member) | ForEach-object {write-host $_.name}
CompareTo
Equals
...

Имея только 21 объект, выполнение вышеупомянутых проблем не составляет проблем. Но что, если процесс генерирует сотни тысяч объектов? Тогда я не хочу запускать процесс один раз, чтобы просто посчитать объекты, а затем запустить его снова, чтобы выполнить то, что я хочу с ними сделать. Итак, как я могу получить количество объектов в коллекции, отправленной по конвейеру?

A аналогичный вопрос был задан ранее, и принятым ответом было использование переменной counter внутри блока скриптаэто работает на коллекции. Проблема в том, что у меня уже есть этот счетчик, и я хочу проверить правильность результата этого счетчика. Поэтому я не хочу просто считать внутри блока скрипта. Мне нужна отдельная, независимая мера размера коллекции, которую я отправил по конвейеру. Как я могу это сделать?

1 Ответ

1 голос
/ 26 октября 2019

Если требуется обработка подсчета и :

Выполнение собственного подсчета в блоке сценария ForEach-Object - лучший способ избежать обработки в два прохода.

Проблема в том, что у меня уже есть этот счетчик, и я хочу проверить правильность результата этого счетчика.

ForEach-Object надежно вызывается для каждого входного объекта, включая значения $null, поэтому нет необходимости перепроверять.

Если вы хотите очиститель разделение обработки и подсчета, вы можете передать несколько -Process блоков сценария в ForEach-Object (в этом примере { $_ + 1 } - это ввод- обработка блок сценария, а { ++$count } - это входные данные - , считая один):

PS> 1..5 | ForEach-Object -Begin { $count = 0 } `
                          -Process { $_ + 1 }, { ++$count } `
                          -End { "--- count: $count" }

2
3
4
5
6
--- count: 5

Обратите внимание, что из-за странности в привязке параметров ForEach-Object, передается -Begin и-End блоки сценариев на самом деле требуются для того, чтобы передать несколько блоков -Process (на объект ввода);pass $null если вам на самом деле не нужны -Begin и / или -End - см. этот выпуск GitHub .

Также обратите внимание, что переменная $count живет в вызывающейобласть действия и не ограничивается вызовом ForEach-Object;то есть $count = 0 потенциально обновляет существующую переменную $count и, если она ранее не существовала, продолжает работать после вызова ForEach-Object.


Если необходим только счет:

Measure-Object - это командлет для использования с большими потоковыми коллекциями ввода в конвейере [1] :

В следующем примере генерируется 100 000 целых чисел одно за другим и Measure-Object подсчитывает их одно за другим, не собирая весь ввод в память.

PS> (& { $i=0; while ($i -lt 1e5) { (++$i) } } | Measure-Object).Count
100000

Caveat : Measure-Object игнорирует $null значения во входной коллекции - см. этот выпуск GitHub .

Обратите внимание, что при подсчете входных объектовПоведение по умолчанию Measure-Object, оно также поддерживает множество других операций, таких как суммирование -Sum и усреднение (-Average), опционально комбинируемых в одном вызове.


[1] Measure-Object, как командлет , способен обрабатывать ввод в потоке fashion, что означает, что он считает объекты, которые он получает один за другим по мере их получения, что означает, что даже очень большие потоковые входные наборы (те, которые также созданы один за другим, такие как перечисление строкбольшого CSV-файла с Import-Csv) можно обрабатывать без риска нехватки памяти - нет необходимости загружать входную коллекцию в целом в память. Однако, если (а) входной набор уже имеет в памяти, или (b) он может уместиться в память и производительность важна, тогда используйте (...).Count.

...