Командлет, который возвращает несколько объектов, какой это тип коллекции, если есть? [PowerShell] - PullRequest
2 голосов
/ 01 февраля 2020

Пример командлета Get-ADuser:

$Users = Get-ADuser -Filter *

В большинстве случаев он возвращает несколько объектов ADuser, но какой это тип "коллекции"? В документации только сказано, что он вернет один или несколько пользовательских объектов Microsoft.ActiveDirectory.Management.ADUser.

Пытался использовать, например, ($Users -is [System.Collections.ArrayList]), но я не могу прибить тип "collection"?

Ответы [ 2 ]

3 голосов
/ 01 февраля 2020

Командлеты сами обычно используют нет тип сбора в своих выходных данных . [1] :
Они излучают отдельные объекты к конвейеру , которые могут ситуационно означать: 0 (нет), 1 или несколько объектов.

Это точно что делает Get-ADUser: указанное c количество выходных объектов зависит от аргументов, которые были переданы ; именно поэтому Get-AdUser help topi c упоминает только скалярный тип ADUser в качестве выходного типа и заявляет, что он «возвращает один или несколько» из них.

Обычно конвейер PowerShell представляет собой поток объектов , конкретная длина которых (число объектов) не должна быть заранее известна с командами при последующей обработке сегментов конвейера. выходные объекты предыдущего сегмента один за другим по мере их получения (см. about_Pipelines ).


Однако это PowerShell engine , который автоматически собирает несколько выходов для вас в массиве [object[]] [2] при необходимости , особенно если вы захватить вывод через присвоение переменной или использовать командный вызов через (...) как часть выражения :

# Get-ChildItem C:\Windows has *multiple* outputs, so PowerShell
# collects them in an [object[]] array.
PS> $var = Get-ChildItem C:\Windows; $var.GetType().Name
Object[]

# Ditto for an expression (...)
PS> (Get-ChildItem C:\Windows).GetType().Name
Object[]

Однако, если данная команда - возможно ситуативно - выводит только один объект тогда вы получите только этот объект сам - он не , заключенный в массив:

# Get-Item C:\ (always) returns just 1 object.
PS> $var = Get-Item C:\; $var.GetType().Name
DirectoryInfo # *not* a single-element array, 
              # just the System.IO.DirectoryInfo instance itself

Что может быть сложным, так это то, что данная команда может ситуационно создавать один или несколько выходов, в зависимости от входов и условий выполнения, поэтому двигатель может возвращать либо один объект , либо массив.

# !! What $var receives depends on the count of subdirs. in $HOME\Projects:
PS> $var = Get-ChildItem -Directory $HOME\Documents; $var.GetType().Name
??? # If there are *2 or more* subdirs: an Object[] array of DirectoryInfo instances.
    # If there is only *one* subdir.: a DirectoryInfo instance itself.
    # (See below for the case when there is *no* output.)

@(), оператор подвыражения массива , предназначен для устранения этой неоднозначности , если необходимо: Оборачивая команду в @(...), PowerShell гарантирует, что ее вывод всегда , собранный как [object[]] - даже если команда выдаст только один выходной объект или даже none :

PS> $var = @(Get-ChildItem -Directory $HOME\Projects); $var.GetType().Name
Object[] # Thanks to @(), the output is now *always* an [object[]] array.

При присвоении переменных потенциально более эффективной альтернативой является использование ограничения типа [array] , чтобы обеспечить выходной массив:

# Alternative to @(...)
PS> [array] $var = Get-ChildItem -Directory $HOME\Documents; $var.GetType().Name
Object[]

Примечание:

  • Это потенциально более эффективно в этом если RHS уже является массивом, он присваивается как есть, тогда как @(...) на самом деле перечисляет выходные данные ... и затем собирает элементы в новый ([object[]]) массив.

    • [array] сохраняет указанный c тип входного массива, просто пропуская его через (например, [array] $var = [int[]] (1..3) хранит массив [int[]] как есть в $var).
  • Помещение [array] «броска» в left из $var = ... - что делает его ограничением типа для переменной - означает, что тип переменной заблокирован в и присваивает различные значения в $var позже продолжит преобразовывать значение RHS в [array] ([object[]]), если необходимо (если вы не назначите $null или "пусто" (см. ниже)).


Если команда выдаст no output, вы получите «ничто» (строго говоря: [System.Management.Automation.Internal.AutomationNull]::Value singleton), что в большинстве случаев ведет себя как $null [3] :

# Get-Item nomatchingfiles* produces *no* output.
PS> $null -eq (Get-Item nomatchingfiles*)
True

# Conveniently, PowerShell lets you call .Count on this value, which the
# behaves like an empty collection and indicates 0.
PS> (Get-Item nomatchingfiles*).Count
0

[1] Это можно вывести целые коллекции в целом в конвейер (в коде PowerShell с Write-Output -NoEnumerate $collection или, более кратко, , $collection), но тогда это просто еще один объект в конвейере, который оказывается самой коллекцией. Вывод коллекций в целом - это аномалия , однако это меняет то, как команды, которые вы передаете в , видят вывод, что может быть неожиданным; ярким примером является ConvertFrom-Json неожиданное поведение до v7.0 .

[2] a System.Array экземпляр, элементы которого имеют тип System.Object, что позволяет смешивать объекты разных типов в одном массиве.

[3] Есть сценарии ios, в котором «ничто» ведет себя не так, как $null, особенно в конвейере и в операторах switch, как подробно описано в этом комментарии к GitHub ; связанная проблема - это запрос функции, чтобы сделать «ничто» более легко отличимым от $null, поддерживая -is [AutomationNull] в качестве теста.

1 голос
/ 01 февраля 2020

Обычно это просто массив объектов:

$Users.gettype()
$Users.count
$Users -is [object[]]

$ ошибка - массив данных:

$error -is [collections.arraylist]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...