对象输出的奇怪延迟后跟开始睡眠(或直到脚本结束)

js2*_*010 3 powershell sleep object output

出于某种原因,对象在 sleep 命令完成之前不会输出。

[pscustomobject]@{message = 'hi'}; sleep 5
Run Code Online (Sandbox Code Playgroud)

这是另一个例子。在循环完成之前您不会看到输出。

foreach ($i in 1..60) { 
  if ($i -eq 1) { [pscustomobject]@{message = $i} } 
  sleep 1
}
Run Code Online (Sandbox Code Playgroud)

我猜你必须输出至少 2 个对象才能看到任何东西?¯\_(?)_/¯ 15 秒后,您会看到两个对象。

foreach ($i in 1..60) {
  if ($i -eq 1 -or $i -eq 15) { [pscustomobject]@{message = $i} }
  sleep 1
}
Run Code Online (Sandbox Code Playgroud)

或者输出足够的属性(> 4)来隐式调用格式列表而不是格式表。 格式表是问题所在。 这马上就出来了。

[pscustomobject]@{a=1; b=2; c=3; d=4; e=5}; sleep 10
Run Code Online (Sandbox Code Playgroud)

我想知道格式表的参数是否可以像-NoWait.

具有包含列宽的格式文件的已知对象类型不存在此问题。

foreach ($i in 1..60) { 
  if ($i -eq 1) { get-process powershell } 
  sleep 1
}
Run Code Online (Sandbox Code Playgroud)

或默认为自定义格式的对象:

foreach ($i in 1..60) { 
  if ($i -eq 1) { get-date } 
  sleep 1
}
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

tl;博士

  • 如果命令的输出导致自动表格显示(隐式 Format-Table),则显示输出可能会在特定情况下最多延迟 300 毫秒。(请参阅下文了解原因和时间),这可能会产生两种意想不到的效果:

    • 与问题一样,在延迟过去之前提交的后续 Start-Sleep提交进一步延迟了(至少)睡眠持续时间的输出 - 它有效地暂停了完成 300 毫秒。等待。

    • 一个后续 Write-HostOut-Host调用可以产生意外来输出第一

  • 您可以通过将命令通过管道传输到或显式(或任何其他cmdlet)来强制同步显示输出。Out-HostFormat-TableFormat-*

    • 但是,这样做意味着仅生成用于显示的输出,这意味着您无法(有意义地)捕获或中继命令的输出。
# The Out-Host forces instant display, before sleeping starts.
# However, use of Out-Host means you can't capture the output.
[pscustomobject] @{message = 'hi'} | Out-Host; sleep 5
Run Code Online (Sandbox Code Playgroud)

这种行为可以通过隐式应用Format-Table输出的臭名昭著的PSv5+ 异步行为来解释:对于没有预定义格式数据且具有4 个或更少属性(这是自动选择显示的属性)的数据类型,它等待最多 300 毫秒。在显示输出之前,努力确定合适的列宽。

如果您Start-Sleep在该时间段过去之前使用,只要您在睡觉,您就会暂停等待。

碰巧没有触发隐式Format-Table格式的输出对象不受影响,但是:

# Immediate output, before sleeping ends:

# Out-of-band formatting of a .NET primitive.
PS> 1; Start-Sleep 5

# Implicit Format-*List* formatting due to having 5+ properties.
PS> [pscustomobject]@{a=1; b=2; c=3; d=4; e=5}; sleep 10
Run Code Online (Sandbox Code Playgroud)

相比之下,由于您的命令的输出是一个只有1 个属性的对象,并且其类型 ( [pscustomobject]) 没有与之关联的预定义格式数据,因此它会触发隐式Format-Table格式并因此出现问题。

简而言之:以下命令输出受到影响,因为它们Format-Table在缺少预定义列宽的情况下选择了隐式输出,因此需要延迟:

  • 类型恰好具有4 个或更少属性的对象

  • 如果这些类型没有关联的预定义格式数据(请参阅 参考资料about_Format.ps1xml),这对于[pscustomobject]实例来说通常是正确的。

    • 此外,但远不如通常,类型格式化的数据默认到表视图,但没有列的宽度预先确定,也受到影响(例如,System.Guid类型实例的是New-Guid输出)。

具有5 个或更多属性的没有格式化数据的类型默认为隐式应用,其中,由于逐行输出,无需确定有用的列宽,因此没有延迟。Format-List


请注意,这只是一个显示问题,如果命令被捕获或发送到管道,数据立即输出(尽管该命令在Start-Sleep时间段过去之前不会整体完成):

# The ForEach-Object command's script block receives the [pscustomobject]
# instance right away (and itself prints it *immediately* to the display, 
# due to outputting a *string* (which never triggers the asynchronous behavior).
& { [pscustomobject]@{message = 'hi'}; sleep 5 } | ForEach-Object { "[$_]" }
Run Code Online (Sandbox Code Playgroud)

虽然有多种方法可以强制同步(立即)显示输出,但它们都改变了命令的基本行为

# Piping to Out-Host:
# Directly prints to the *display* (host).
# No way for a caller to capture the result or for processing
# the result in a pipeline.
[pscustomobject]@{message = 'hi'} | Out-Host; sleep 5

# Using Write-Host:
# Prints directly to the *display* (host) by default.
# While it *is* possible to capture the result via output stream 6.
# the information stream (6> file.txt), that output:
#  * is invariably converted to *strings*
#  * and the string representation does *not* use the friendly default
#    output formatting; instead, the objects are stringified with simple
#    [psobject.].ToString() calls, which results in a much less friendly
#    representation.
Write-Host ([pscustomobject]@{message = 'hi'}); sleep 5

# Piping to a Format-* cmdlet explicitly:
# While this does write to the success-output stream (stream number 1),
# as the command would by default, what is written isn't the original
# objects, but *formatting instructions*, which are useless for further
# programmatic processing.
# However, for redirecting the output to a file with Out-File or >
# this makes no difference, because they convert the formatting instructions
# to the strings you would see on the screen by default.
# By contrast, using Set-Content or any other cmdlet that expects actual data
# would not work meaningfully.
[pscustomobject]@{message = 'hi'} | Format-Table; sleep 5
Run Code Online (Sandbox Code Playgroud)


js2*_*010 0

输出少于5个属性,format-table隐式运行。在显示第一个对象之前,格式表将无限期地等待第二个对象。这适用于没有定义默认表视图的 xml 文件的对象类型(如 pscustomobject)。

# no output for 5 seconds

&{get-date
sleep 5
get-date} | format-table


DisplayHint DateTime                               Date                 Day DayOfWeek DayOfYear Hour  Kind Millisecond Minute
----------- --------                               ----                 --- --------- --------- ----  ---- ----------- ------
   DateTime Saturday, February 8, 2020 10:24:48 AM 2/8/2020 12:00:00 AM   8  Saturday        39   10 Local         618     24
   DateTime Saturday, February 8, 2020 10:24:53 AM 2/8/2020 12:00:00 AM   8  Saturday        39   10 Local         892     24
Run Code Online (Sandbox Code Playgroud)

与格式列表比较:

& {get-date
sleep 5
get-date} | format-list 

DisplayHint : DateTime
Date        : 2/8/2020 12:00:00 AM
Day         : 8
DayOfWeek   : Saturday
DayOfYear   : 39
Hour        : 20
Kind        : Local
Millisecond : 408
Minute      : 37
Month       : 2
Second      : 18
Ticks       : 637167910384087860
TimeOfDay   : 20:37:18.4087860
Year        : 2020
DateTime    : Saturday, February 8, 2020 8:37:18 PM

DisplayHint : DateTime
Date        : 2/8/2020 12:00:00 AM
Day         : 8
DayOfWeek   : Saturday
DayOfYear   : 39
Hour        : 20
Kind        : Local
Millisecond : 662
Minute      : 37
Month       : 2
Second      : 23
Ticks       : 637167910436622480
TimeOfDay   : 20:37:23.6622480
Year        : 2020
DateTime    : Saturday, February 8, 2020 8:37:23 PM
Run Code Online (Sandbox Code Playgroud)