我正在尝试执行一些简单的if语句,但所有基于[Microsoft.Management.Infrastructure.CimInstance]的新cmdlet似乎都没有公开.count方法?
$Disks = Get-Disk
$Disks.Count
不归还任何东西。我发现我可以将它转换为[数组],这使得它按预期返回.NET .count方法。
[Array]$Disks = Get-Disk
$Disks.Count
这可以直接将其作为以前cmdlet的数组投射:
(Get-Services).Count
推荐的解决方法是什么?
一个不起作用的例子:
$PageDisk = Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)}
If ($PageDisk.Count -lt 1) {Write-Host "No suitable drives."; Continue}
Else If ($PageDisk.Count -gt 1) {Write-Host "Too many drives found, manually select it."}
Else If ($PageDisk.Count -eq 1) { Do X }
选项A(演员阵容):
[Array]$PageDisk = Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)}
If ($PageDisk.Count -lt 1) {Write-Host "No suitable drives."; Continue}
Else If ($PageDisk.Count -gt 1) {Write-Host "Too many drives found, manually select it."}
Else If ($PageDisk.Count -eq 1) { Do X }
选项B(使用数组索引):
$PageDisk = Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)}
If ($PageDisk[0] -eq $Null) {Write-Host "No suitable drives."; Continue}
Else If ($PageDisk[1] -ne $Null) {Write-Host "Too many drives found, manually select it."}
Else If (($PageDisk[0] -ne $Null) -and (PageDisk[1] -eq $Null)) { Do X }
选项C(数组) - 感谢@PetSerAl:
$PageDisk = @(Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)})
If ($PageDisk.Count -lt 1) {Write-Host "No suitable drives."; Continue}
Else If ($PageDisk.Count -gt 1) {Write-Host "Too many drives found, manually select it."}
Else If ($PageDisk.Count -eq 1) { Do X }
基于CIM的cmdlet不暴露.Count方法的原因是什么?建议的方法是什么?选项B对我来说似乎很复杂,很难阅读。选项A有效,但是不应该将PowerShell转换为数组吗?我是以完全错误的方式解决这个问题吗?
答案 0 :(得分:7)
在PSv3 +中,通过统一处理标量和集合,任何对象 - 甚至$null
- 应该拥有.Count
属性(以及$null
除外,应支持使用[0]
进行索引。
任何出现支持上述内容的 的对象都应被视为 bug 。
例如,不按这些规则播放的[pscustomobject]
个实例是known bug。
由于我不知道所述错误是否与[Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Disk]
输出的Get-Disk
个实例有关,而且Get-Disk
至少是当前的@(...)
实例 - 仅在 Windows PowerShell ,我建议您在uservoice.com上单独提交错误。
使用array-subexpression运算符.Count
仅 ::
作为上述错误的 解决方法 。
以防标量对象碰巧拥有拥有 @(...)
属性。
通常,如果您确实需要确保某些内容是数组,使用[Array] ...
而不是[object[]] ...
/ @()
- @()
是PowerShell惯用的,更简洁,语法更容易。
也就是说,假设[Array]
在技术上创建了现有数组的(浅)副本,在处理可能较大的数组时,您可能更喜欢@(...)
。功能
此外, [Array] ...
和@($null)
通常不等同于 ,因为PetSerAl是一个有用的示例评论问题证明;适应他的一个例子:
$null
返回单项数组,其唯一元素为[Array] $null
,而$null
无效(保留@()
)。
$null
的这种行为与其目的一致(见下文):由于@()
不是数组,[System.Object[]]
将其包装成一个(导致$null
以@()
为唯一元素的实例。
在PetSerAl的其他例子中,New-Object
@(...)
的行为 - 创建的数组和集合 - 可能会令人惊讶 - 见下文。
@()
的目的及其运作方式:松散地说,@()
(数组子表达式运算符)的目的是确保将表达式/命令的结果视为数组 ,即使它恰好是标量(单个对象)。
更确切地说,,
的行为如下:向PetSerAl提供大量帮助的提示。
在 PSv5.1 + 中,使用使用 @()
使用直接构建数组的表达式 (数组表达式运算符)优化@(1, 2)
:
例如,1, 2
与@(, 1)
相同,, 1
与,
相同。
对于使用 System.Object[]
构造的数组 - 产生@( ..., ..., ...)
数组 - 这种优化很有帮助,因为它节省了不必要的步骤首先展开该阵列,然后重新包装(见下文)
据推测,这种优化是由于使用@()
来构造数组的广泛且以前效率低下的做法所促成的,源于错误地认为构造数组需要[int[]]
。
但是,在仅限Windows PowerShell v5.1 中,使用构建具有特定类型的阵列时,也会应用优化强制转换 ,例如@([int[]] (1, 2)).GetType().Name
(PowerShell Core 中的行为已得到纠正,旧的Windows PowerShell版本不受影响);例如,
Int32[]
会产生@()
。这是System.Object[]
返回其他而不是@([int[]] (1, 2))[-1] = 'foo'
的唯一情况,并假设它总是会导致意外错误和副作用 ;例如: -
$a = [int[]] (1, 2); $b = @([int[]] $a)
休息
@(...)
意外地未创建新数组 - 请参阅this GitHub issue。
否则:如果[System.Object[]]
中的(第一个)语句是表达式,则使用PowerShell的标准集合收集它 - 展开(展开)技术;然而,命令的输出被解释为原样;在任何一种情况下,结果对象的数量决定了行为:
如果结果是单项目/包含否项目,则结果包裹在单个项目中元素/空数组@('foo').GetType().Name
。
例如,Object[]
产生@('foo').Count
和1
产生'foo'.Count
(但如上所述,在PSv3 +中,您可以直接使用@( & { } ).Count
) 。
0
产生[System.Management.Automation.Internal.AutomationNull]::Value
(执行空脚本块输出" null集合"
(@()
)
警告:围绕New-Object
调用的 @(New-Object System.Collections.ArrayList).Count
创建一个数组/集合输出数组/集合包裹在单个 - 元素外部数组 。
1
产生System.Object[]
- 空数组列表包含在单个元素New-Object
实例中。原因是@()
由于是命令(例如 cmdlet 调用)而不是需要解包,导致@([system.collections.arraylist]::new()).Count
只看到单个项(恰好是一个数组/集合),因此它包含在一个单项数组中。
当您使用表达式构建数组/集合时,会发生 not ,这可能令人困惑,因为表达式是' s输出解包(展开):
0
收益@()
;表达式输出一个空集合,该集合展开为不包含任何项目的结果,System.Object[]
重新打包为空(...)
数组。New-Object
)和New-Object
- 这会转换@((New-Object System.Collections.ArrayList)).Count
命令到表达式 - 会产生相同的结果:0
也会产生[System.Object[]]
。 如果结果包含多个项,则这些项将作为数组返回,(通常在WinPSv5.1中)并非总是 - 请参阅上文)作为常规PowerShell数组($arr = [int[]] (1, 2); @($arr)
):
[int[]]
展开$arr
数组System.Object[]
,然后将元素重新打包为@()
数组。[int[]]
中,实际上会得到一个{{1}}数组。)答案 1 :(得分:2)
@mklement0 有一个很好的答案,但要补充一点:如果您的脚本(或调用您的脚本)具有 Set-StrictMode
,自动 .Count
和 .Length
属性将停止工作:
$ Set-StrictMode -off
$ (42).Length
1
$ Set-StrictMode -Version Latest
$ (42).Length
ParentContainsErrorRecordException: The property 'Length' cannot be found on this object. Verify that the property exists.
为了安全起见,您可以在检查长度之前将任何未知变量包装在数组 @(...)
中:
$ $result = Get-Something
$ @($result).Length
1