返回多个对象的 cmdlet,它是什么集合类型(如果有)?[电源外壳]

MrC*_*vin 3 arrays collections powershell cmdlet

Get-ADusercmdlet的示例:

$Users = Get-ADuser -Filter *
Run Code Online (Sandbox Code Playgroud)

在大多数情况下它会返回多个 ADuser 对象,但它是什么“集合”类型?文档只说它会返回一个或多个 Microsoft.ActiveDirectory.Management.ADUser 的用户对象。

尝试使用例如,($Users -is [System.Collections.ArrayList])但我无法确定“集合”类型?

mkl*_*nt0 6

Cmdlet本身通常在其输出中使用集合类型[1]
它们向管道发出单个对象,这在特定情况下可能意味着:(无)、 或多个。01

这正是Get-ADUser它的作用:输出对象的具体数量取决于给定的参数;这就是为什么 Get-AdUser帮助主题只提到标量类型ADUser作为输出类型并声明它“返回一个或多个”的原因。

通常,PowerShell 管道是一个对象流,其特定长度(对象计数)不需要事先知道,后续管道段中的命令会在接收到前一个段的输出对象时一个一个地处理它们(请参阅about_Pipelines)。


然而,它是PowerShell的发动机自动收集为您的多个输出中的一个[object[]]阵列[2] 如果需要的话,通过特别是如果捕获输出变量赋值,或者使用经由命令呼叫(...)时,分组操作者(或$(...),所述子表达式运算符[3 ] ),作为表达式

# 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 with (...) (and also with $(...) and always with @(...))
PS> (Get-ChildItem C:\Windows).GetType().Name
Object[]
Run Code Online (Sandbox Code Playgroud)

然而,如果给定的命令-可能situationally -仅输出单个对象,你会再拿到只是对象本身-它不是包裹在一个阵列:

# 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
Run Code Online (Sandbox Code Playgroud)

棘手的是,根据输入和运行时条件,给定的命令可以根据情况产生一个或多个输出,因此引擎可能返回单个对象数组。

# !! 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.)
Run Code Online (Sandbox Code Playgroud)

@()数组子表达式运算符,旨在消除这种歧义,如果需要:通过将命令包装在 中@(...),PowerShell 确保其输出始终被收集为[object[]]- 即使该命令碰巧只产生一个输出对象或什至没有

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

对于变量赋值,一种可能更有效的替代方法是使用[array] 类型约束来确保输出成为数组:

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

笔记:

  • 这可能更有效,因为如果 RHS 已经恰好是一个数组,则按原样分配它,而@(...)实际上枚举来自的输出...,然后元素重新组装到一个新的( [object[]]) 数组中。

    • [array] 通过简单地传递它来保留输入数组的特定类型(例如,[array] $var = [int[]] (1..3)[int[]]数组按原样存储在中$var)。
  • 配售[array]“投”到$var = ...-这正是它使一个类型约束的变量-意味着变量的类型锁定,并指定不同的价值$var以后将继续在RHS值转换[array][object[]]) , 如果需要(除非您分配$null或“没有”(见下文))。


如果命令不产生任何输出,您将得到“无”(严格来说:[System.Management.Automation.Internal.AutomationNull]::Value单例),在大多数情况下$null,其行为类似于[4]

# 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
Run Code Online (Sandbox Code Playgroud)

[1]它能够输出的整个集合作为一个整体到管线(与PowerShell代码Write-Output -NoEnumerate $collection或更简洁地,, $collection),但是这是然后在恰好是一个集合本身管道只是另一个对象。将集合作为一个整体输出是一种异常情况,但是,这会改变您通过管道查看输出的命令的方式,这可能是意料之外的;一个突出的例子是ConvertFrom-Jsonv7.0 之前的意外行为

[2]System.Array元素类型为 的实例System.Object,允许您在单个数组中混合不同类型的对象

[3] 使用(...)通常就足够了;$(...)仅在字符串插值(可扩展字符串)和在更大的表达式中嵌入整个语句或多个命令时才需要;请注意$(...),与(...)它本身不同的是,它会解开单元素数组;比较(, 1).GetType().Name$(, 1).GetType().Name; 看到这个答案

[4] 在某些情况下,“无”的行为与 不同$null,特别是在管道和switch语句中,如GitHub 上的此评论中所述;链接的问题是一个功能请求$null,通过支持-is [AutomationNull]作为测试,使“无”更容易与 区分。