【问题标题】:weird delay of the output of an object when followed by start-sleep (or until script end)开始睡眠(或直到脚本结束)后对象输出的奇怪延迟
【发布时间】:2020-04-07 09:02:37
【问题描述】:

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

[pscustomobject]@{message = 'hi'}; sleep 5

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

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

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

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

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

[pscustomobject]@{a=1; b=2; c=3; d=4; e=5}; sleep 10

我想知道是否可以像-NoWait 这样将参数添加到格式表中。

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

foreach ($i in 1..60) { 
  if ($i -eq 1) { get-process powershell } 
  sleep 1
}

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

foreach ($i in 1..60) { 
  if ($i -eq 1) { get-date } 
  sleep 1
}

【问题讨论】:

  • 这能回答你的问题吗? Unable to Pause or Sleep after Select-Object
  • @bacon 我可以睡觉了,就是不能及时看到输出。
  • @js2010 这个问题的要点不是Pause/Sleep 根本没有发生,而是它们发生在之前一个产生的命令尽管在 之后调用了输出,这似乎与您在此处描述的问题相同。可能有更好的重复建议,但这个问题似乎是与许多重复相关的问题。再说一次,我不确定我是否知道这个网站上的潜在重复内容(如果它基于一个常见的问题或相互适用的答案).. .
  • @BACON,另一个几乎重复的是stackoverflow.com/a/43691123/45375,它链接到你提到的帖子。问题在于,有时根本原因可能是相同的,但触发场景和症状有很大不同,并且与根本原因没有明显联系,因此需要具体解释。我的个人 M.O.是:解决答案中的特定场景,但链接到解释根本原因的近乎重复的内容。

标签: powershell object output sleep


【解决方案1】:

tl;dr

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

    • 与问题一样,在延迟过去之前提交的后续Start-Sleep 会进一步延迟(至少)睡眠期间的输出 - 它实际上暂停 完成 300 毫秒。等等。

    • 后续Write-HostOut-Host 调用可能会产生出乎意料的输出首先

  • 您可以强制同步显示输出,方法是将命令显式传送到Out-HostFormat-Table(或任何其他Format-* cmdlet)。

    • 但是,这样做意味着仅生成 for-display 输出,这意味着您将失去(有意义地)捕获或中继命令输出的能力。
# 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

臭名昭著的PSv5+ asynchronous behavior of implicitly applied Format-Table output 解释了这种行为:对于具有4 个或更少属性 的数据类型没有预定义的格式化数据(这是自动选择table 显示),它最多等待 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

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

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

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

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

    • 另外,但不太常见的是,带有格式数据的类型默认为表格视图,但没有预定义列宽度,是也受到影响(例如,New-Guid 输出的 System.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 { "[$_]" }

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

# 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

【讨论】:

  • 很好的答案。解释得很好,很容易理解。谢谢
  • 我很高兴听到这个答案很有帮助,我感谢您的反馈,@adampski。
【解决方案2】:

将您的自定义对象通过管道传递给Out-Host cmdlet:

[pscustomobject]@{message = 'hi'} | Out-Host; sleep 5

当您使用Out-Host cmdlet 时,您会立即向主机显示结果。没有它,对象将输出到管道,直到 Start-Sleep cmdlet 之后才会返回。

【讨论】:

  • 对象被输出到管道直到Start-Sleep cmdlet之后才返回只有情景为真,并且知道什么时候适用是OP的问题所在。另外值得一提的是,使用Out-Host意味着命令输出直接打印到显示器(主机),并且不能再被捕获/重定向。
【解决方案3】:

输出少于 5 个属性,并且 format-table 隐式运行。 Format-table 将在显示第一个对象之前等待第二个对象的 无限期 时间。这适用于没有定义 default 表视图的 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

与格式列表比较:

& {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

【讨论】:

    猜你喜欢
    • 2011-05-11
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    相关资源
    最近更新 更多