为什么数组在直接分配或从 get-content 检索时表现不同

Why does an array behave differently when directly assigned or retrieved from get-content

这里有一点我不明白。

当我定义一个变量时:

$v = [byte]2, [byte]3

并检查其类型:

$v.getType().name

我明白了

Object[]

然后我格式化$v:

'{0} {1}' -f $v

打印

2 3

现在,如果我得到文件的前两个字节:

$f = (get-content 'xyz.txt' -encoding byte -readCount 2 -totalCount 2)

并检查其类型:

$f.getType().name

我得到了和以前一样的类型:Object[].

但是,与 $v 不同,我无法格式化 $f:

'{0} {1}' -f $f

我收到错误消息格式化字符串时出错:索引(从零开始)必须大于或等于零且小于 的大小,尽管 的长度数组是 2:

$f.length

returns

2

我不明白这是为什么,希望得到解释。

  • 行为应被视为 -f 运算符 中的 错误;它从 v7.1 开始存在并在 GitHub issue #14355 中报告;它 不会 影响其他具有数组操作数的运算符,例如 -split-in.

  • 解决方法 是将 $f 转换为 [array] 或者,如果创建 copy 的数组是可以接受的,@($f):

'abc' > xyz.txt

$f = get-content 'xyz.txt' -encoding byte -readCount 2 -totalCount 2

'{0} {1}' -f ([array] $f)

注意:使用 @()array-subexpression operator - ... - @($f) - as Mathias R. Jessen 注释 - 是更简单的选项,但请注意使用 @() 涉及 克隆 (创建一个浅表副本)数组,而在这种情况下 [array] 转换没有。

另一种方法是将 [array] 强制转换应用为 类型约束 (将其放置在 $f = ... 赋值的左侧):

'abc' > xyz.txt

[array] $f = (get-content 'xyz.txt' -encoding byte -readCount 2 -totalCount 2)

'{0} {1}' -f $f

注:

  • 在 PowerShell [Core] v6+ 中,您必须使用 -AsByteStream 代替 -Encoding Byte

  • 如果省略-ReadCount 2也可以避免这个问题,但请注意,这会降低命令的性能,因为字节是一个一个地发出的;也就是说,使用 -ReadCount 2 -TotalCount 2 发出一个 单个 对象,它是一个 2 字节数组 作为一个整体 ,而只是 -TotalCount 2 单个字节 逐个 发送到管道,在这种情况下,PowerShell 引擎本身会收集这些字节[object[]] 赋值数组。

  • 请注意,将 @() 直接应用于 命令 - @(get-content ...) - 将 而不是 在这种情况下有效,因为 @(),由于参数组合 -ReadCount 2 -TotalCount 2,接收到 单个 输出对象 恰好是一个数组作为一个整体,因此将单个对象包装在另一个数组中。这会产生一个单元素数组,其元素是原始的 2 元素字节数组;有关 @(...) 工作原理的更多信息,请参阅


背景资料:

问题是 Get-Content -ReadCount 返回的每个数组的不可见 [psobject] 包装器 (只是 中的一个 这种情况下),这意外地导致传递给 -f$f 数组 不被识别。

请注意,PowerShell 的其他基于数组的运算符,例如 -in-replace 受到影响。

可以通过两种方式绕过包装器:

  • $f.psobject.BaseObject

  • 转换为 [array],如顶部所示。

注:

  • 通常,cmdlets 生成的输出对象 - 与 PowerShell 生成的输出相反代码 - 通常不可见[psobject]包装器;大多数情况下,它们是良性的,因为 PowerShell 通常只关心 .NET 对象被 wrapped,而不关心包装器,但有时会出现问题,例如在这种情况下 - 请参阅 GitHub issue #5579 用于讨论问题及其出现的其他上下文。

  • 为了测试给定对象是否有[psobject]包装器,使用-is [psobject];例如:

$var = 1
$var -is [psobject] # -> $false

$var = Write-Output 1
$var -is [psobject] # -> $true, due to use of a cmdlet.

# You can also test command output directly.
(Write-Output 1) -is [psobject]  # -> $true