是什么导致 select-object 的输出被截断?

What causes the output of select-object to be truncated?

我有以下愚蠢的 PowerShell 脚本:

$username = 'rny'

$null = mkdir "c:\Users$username\YYY"
$null = mkdir "c:\Users$username\YYY\TODO"
$null = mkdir "c:\Users$username\YYY\TODO21-12-22_Foo-bar-baz-etc"

$files = "C:\Users$username\one-two-three-four.sql.wxyz",
         "C:\Users$username\YYY\TODO21-11_29_abcdefghijklmnop.wxyz",
         "C:\Users$username\YYY\TODO21-12-22_Foo-bar-baz-etc\another-filename.wxyz"

foreach ($file in $files) {
   $null = new-item $file
}

  Get-ChildItem . -errorAction silentlyContinue -recurse -filter *.wxyz | select-object fullName 

foreach ($file in $files) {
    remove-item  -literalPath $file
}

rmdir "c:\Users$username\YYY\TODO21-12-22_Foo-bar-baz-etc"
rmdir "c:\Users$username\YYY\TODO"
rmdir "c:\Users$username\YYY"

当我执行它时,get-childItem ... | select-object 管道的输出被截断:

FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO21-11_29_abcdefghijklmnop.wxyz
C:\Users\rny\YYY\TODO21-12-22_Foo-bar-baz-etc\an...

特别注意最后一行。此行为已被记录 elsewhere on SuperUser,可接受的答案是使用 -autoSize 将输出通过管道传输到 format-table。到目前为止,还不错。

但是,如果我像这样在 $files 数组的赋值中注释第二个文件

$files = "C:\Users$username\one-two-three-four.sql.wxyz",
#        "C:\Users$username\YYY\TODO21-11_29_abcdefghijklmnop.wxyz",
         "C:\Users$username\YYY\TODO21-12-22_Foo-bar-baz-etc\another-filename.wxyz"

输出不再被截断:

FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO21-12-22_Foo-bar-baz-etc\another-filename.wxyz

这让我感到困惑,因为被截断的文件的名称现在完全可见,对此我没有任何解释。

那么,究竟是什么原因导致文件在一种情况下被截断,而在另一种情况下却没有?

这与 Select-Object 本身没有太大关系 - 它更多地与 PowerShell 如何将值转换为字符串表示形式有关,特别是在这种情况下,当它显示未捕获的输出时它是如何做到的控制台上的 cmdlet。

PowerShell (Windows and Core) 有一堆预配置的“视图”,定义了一些 built-in 类型的呈现方式——例如他们是使用 Format-List 还是 Format-Table,要显示什么属性,在 table 的情况下,显示每列的宽度 - 请参阅 about_Format.ps1xml.

对于其他类型,PowerShell 会尝试动态生成 best-guess。为此,它等待输入中的前 N ​​个项目到达,以决定要应用的格式规则。我找不到任何明确的文档说明 PowerShell 等待多少项,所以这可能是一个很好的 follow-up 问题 :-)。

显然,您可以通过为 Format-TableFormat-List 传递格式化参数来覆盖这些默认值。

在您的情况下,top-level 脚本已收到包含 PSCustomObject objects 数组的管道输出(即 Select-Object 的输出),并决定显示它们在 table 中有一列用于 FullName 属性.

示例 1

在您的第一个示例中,它查看了前两个 PSCustomObject 项目并决定将 FullName 列设为 54 个字符宽,因为这是 C:\Users\rny\YYY\TODO21-11_29_abcdefghijklmnop.wxyz 的长度,第三个项目被截断为相同 宽度(如果包含 ...),因为它未包含在列宽的 decision-making 过程中。

FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO21-11_29_abcdefghijklmnop.wxyz
C:\Users\rny\YYY\TODO21-12-22_Foo-bar-baz-etc\an...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| 54 characters

示例 2

在您的第二个示例中,PowerShell 发现前几个 PSCustomObjects 中最长的 FullName 属性 是 C:\Users\rny\YYY\TODO21-12-22_Foo-bar-baz-etc\another-filename.wxyz,因此使用的列宽为 70。

FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO21-12-22_Foo-bar-baz-etc\another-filename.wxyz
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| 70 characters

示例 3

最后,如果您按照@notjustme 在评论中的建议将 -ExpandProperty FullName 添加到 Select-Object 上,您将得到一个 string 值数组而不是 [=18= 数组]s 这就是为什么您可能会看到 PowerShell 应用不同的格式规则 - 例如您没有得到 FullName header 因为值是字符串而不是 objects 属性,并且它使用 Format-List 而不是 Format-Table.

C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO21-12-22_Foo-bar-baz-etc\another-filename.wxyz

添加一些背景:

具体来说,您看到的是臭名昭著的 300 毫秒的 效果。 Format-Table 格式化 中内置的延迟,PowerShell 隐含地 适用于具有 4 个或更少属性且没有关联的显式格式化数据的 .NET 类型的实例他们。

有关详细信息,请参阅 (在 同一问题的不同症状 的上下文中给出,即意外输出 排序),但简而言之:延迟用于根据延迟期间收到的特定 属性 值推断 合适的列宽 .

这意味着 具有 属性 值的对象在 300 毫秒后收到。如果它们的值碰巧 在[=]期间收到的值中最宽的值延迟时间.

具体来说,你的症状意味着只有 前两个 对象在延迟期内被接收,并且两个 属性 值中较长的值​​随后被锁定在列宽;当稍后收到 third 对象时,列宽已被锁定,并且较长的值被截断(在 Windows PowerShell 中用尾随 ... 表示(3 . 个字符。) 和 在 PowerShell (Core) 7+(单个字符))

避免截断的唯一方法是知道最大值。列宽提前 并将其传递给 显式 Format-Table 调用 -
值得注意的是,这会阻止使用输出 作为数据
见下文。


这里有一个引发问题的简单方法

注意:下面的 Select-Object 调用并不是严格需要的,但为了与问题对称而提供。

# Create blocks of two objects with strings of different length in their 
# .Prop value: 10 chars. vs. 100 chars.
$count = 10000 # How often to repeat each object in a row.
$objs = 
 (, [pscustomobject] @{ Prop = ('x' * 10) } * $count) + 
 (, [pscustomobject] @{ Prop = ('y' * 100) } * $count)

# Depending on the value of $count - which translates into how
# long it takes until the second block of objects starts emitting -
# truncation will occur or not.
$objs | Select-Object Prop

对于 10,000 个对象的块,我确实看到了截断:第一个块需要足够长的时间 - 具有较短的 属性 值 - 以锁定显示列的宽度,导致第二个块中的对象被截断:

Prop
----
xxxxxxxxxx
...
yyyyyyyyy…  # <- truncated, because width 10 was locked in during the delay
...

防止截断,将 calculated property 传递给 Format-Table 以指定最大值。宽度:

$objs | Select-Object Prop | Format-Table @{ n='Prop'; e='Prop'; width = 100 }