请注意:
C:\> $x = @(1)
C:\> $x = @($x,2)
C:\> $x = @($x,3)
C:\> $x = @($x,4)
C:\> $x = @($x,5)
C:\> $x.Length
2
C:\> @($x |% { $_ }).Length
3
C:\> $x
Length : 2
LongLength : 2
Rank : 1
SyncRoot : {System.Object[] 2, 3}
IsReadOnly : False
IsFixedSize : True
IsSynchronized : False
Count : 2
4
5
C:\>
Run Code Online (Sandbox Code Playgroud)
我预计管道会压平列表。但这并没有发生。我究竟做错了什么?
我预计管道会压平列表。
PowerShell枚举数组(列表)以及(大多数)可枚举数据类型,即将其元素一一发送到成功输出流(管道充当管道)。
如果一个元素本身恰好是另一个可枚举的,则它也不会被枚举。
将 PowerShell 中的输出概念化为不确定长度的对象流比将其视为数组(列表)及其展平更好- 请参阅底部部分。
另外,仅在用于显示格式期间,可枚举数被枚举为两层深度,因此在视觉上您无法区分@(1, 2)(枚举的数组,导致输出两个整数)和Write-Output -NoEnumerate @(1, 2)(作为输出的数组)单个对象) - 即使在数据输出方面这些命令有所不同。
这种仅用于显示的嵌套枚举在第二层停止,因此该层的任何元素都会按照其本身的格式进行格式化,即使它碰巧是另一个可枚举的元素;例如,对于数组,该数组根据给定对象的常用 PowerShell 规则进行格式化:因为 .NETArray类型具有超过 4 个属性,因此Format-List隐式应用格式化,从而导致逐行显示Array实例的属性,如下例所示:
# The 2nd element - a nested array - is formatted as a whole.
PS> Write-Output -NoEnumerate @(1, @(2))
1
Length : 1
LongLength : 1
Rank : 1
SyncRoot : {2}
IsReadOnly : False
IsFixedSize : True
IsSynchronized : False
Count : 1
Run Code Online (Sandbox Code Playgroud)
将上述内容应用于您的具体示例:
# Your nested array.
$x = @(1); $x = @($x,2); $x = @($x,3); $x = @($x,4); $x = @($x,5)
Run Code Online (Sandbox Code Playgroud)
要可视化生成的嵌套数组,请将其传递给ConvertTo-Json:
PS> ConvertTo-Json -Depth 4 $x
[
[
[
[
[
1
],
2
],
3
],
4
],
5
]
Run Code Online (Sandbox Code Playgroud)
这告诉我们,您已经创建了一个两元素数组,其第一个元素恰好是一个嵌套数组,它本身包含两个元素,其中第一个元素包含另一个嵌套数组。
因此,$x.Length输出2.
@($x | % { $_ }).Length输出3的原因如下:
$x到管道运算符|会枚举其元素,并且输出每个元素(通过( ) 脚本块$_中)会导致每个元素也被枚举(如果它恰好是一个数组)。%ForEach-Object$x被单独输出,然后是5、 的第二个元素$x,总共产生三个输出对象。@(...)) 中捕获这些输出对象会创建一个三元素数组,其.Length报告3.由输出本身产生的用于显示的表示$x遵循上面的解释。
PowerShell管道是成功输出流的管道,成功输出流是不确定长度的对象流,并且仅当捕获该流时- 在分配给变量( $var = ...) 或使命令输出参与表达式的上下文中(例如(...).Foo) -数组的概念是否进入了图片:
[object[]]类型数组。因此,输出流没有数组的概念,最终会导致用数组及其扁平化来讨论它会造成混乱。
请注意,输出流(管道)不仅在使用管道运算符( |)在命令之间显式传输数据时使用,...
...它也可以被任何单个命令隐式使用来发送其输出。
但是,它不用于表达式(单独),例如1 + 2or [int[]] (1, 2, 3)(命令是任何 cmdlet、函数、脚本、脚本块或外部程序)或将参数传递给命令时。
也就是说,如果您将表达式的结果发送到命令 via |(仅当表达式是第一个管道段时才有效),则会再次涉及管道,并且通常的枚举行为(下面讨论)适用于表达式的结果;例如[int[]] (1, 2, 3) | ForEach-Object { 1 + $_ }
也许令人惊讶的是,@(...)and$(...)运算符的使用总是涉及管道,因此即使在其中包含独立表达式也会导致枚举(和重新收集);例如,@([int[]] (1, 2, 3)).GetType().Namereports Object[]( [object[]]),因为枚举了强类型[int[]]数组,并且结果收集在常规 PowerShell 数组中。
唯一的例外是数组文字,例如@(1, 2, 3),其中(在 PowerShell 版本 5 及更高版本中)此行为已被优化掉。
相比之下,该(...)运算符不枚举表达式结果。
默认情况下,将一个数组输出到成功输出流(更一般地说,是实现接口[1]的大多数.NET 类型的实例),会导致它被枚举,即它的元素被一一发送。IEnumerable
因此,输出数组与捕获输出是否也会产生(新)数组之间没有保证的关系。
值得注意的是,输出单个对象与输出包装在(单元素)数组中的单个对象没有区别。
将数组(集合)作为一个整体发送到管道需要额外的工作:
要么使用Write-Output -NoEnumerate
# -> 1, because only *one* object was output, the array *as a whole*
(Write-Output -NoEnumerate @(1, 2) | Measure-Object).Count
Run Code Online (Sandbox Code Playgroud)
或者- 更晦涩但更有效的是,使用 的一元形式,(数组构造函数运算符 ),以便将数组包装在辅助临时数组中,然后该数组的枚举将原始数组输出为单个对象:
(, @(1, 2) | Measure-Object).Count # -> 1
Run Code Online (Sandbox Code Playgroud)
[1] 有关 PowerShell 认为可枚举的类型的摘要(既排除已实现的选定类型IEnumerable,又包括未实现的类型),请参阅此答案的底部部分。
| 归档时间: |
|
| 查看次数: |
527 次 |
| 最近记录: |