【问题标题】:Unable to Pause or Sleep after Select-Object选择对象后无法暂停或睡眠
【发布时间】:2016-01-17 05:10:48
【问题描述】:

在某些情况下,如果我在 Select-Object 命令之后尝试暂停或睡眠,则暂停/睡眠会发生在命令之前。

例如,用

Get-NetAdapter | Select-Object Name,Status
Pause

或

Get-NetAdapter | Select-Object Name,Status | Where-Object {$_ -ne $null}
Pause

输出是:

按 Enter 继续...:

名称状态
---- ------
无线上网
以太网断开

而

Get-NetAdapter | Select-Object Name,Status | Format-Table
Pause

输出是:

名称状态
---- ------
无线上网
以太网断开

按 Enter 继续...:

这里发生了什么?这是错误还是功能?

【问题讨论】:

  • 您使用的是 PowerShell v5 吗?可能是这个问题connect.microsoft.com/PowerShell/Feedback/Details/1635172 那里建议使用Out-Host 的解决方法。
  • pause 使用Read-Host,见Get-Command pause | Format-List
  • v5 中Format-Table 的新功能。 Format-Table 在开始输出之前收集输入 300 毫秒(以找到更好的列宽)。
  • 无法在 Powershell v4 中重现。

标签: powershell


【解决方案1】:

您看到的是新 PowerShell v5 功能的结果。 Format-Table 现在收集输入 300 毫秒以找到更好的列宽。即使您明确指定-AutoSize:$false,它也会以这种方式工作。

当您在命令提示符下键入命令时,该命令会隐式通过管道传送到 Out-Default 命令的单个实例。 Out-Default 命令然后决定如何格式化对象并在 PowerShell 主机(控制台)上打印它们。因此,即使您没有在代码中直接使用Format-Table,也不意味着您的管道中没有Format-Table。 Out-Default 可以决定将对象格式化为表格并在内部使用Format-Table。

具有四个或更少属性且未在格式文件中为它们定义自定义格式的自定义对象被格式化为表格。通过将 Select-Object 与两个属性一起使用,您可以准确地生成该对象。

PowerShell 管道是单线程的。这意味着Format-Table 不能在经过 300 毫秒间隔后才输出所有收集的对象。 Format-Table 必须等到您将下一个项目通过管道传递给它(调用进程块)或报告管道结束(调用结束块)。

PS> Get-NetAdapter | Select-Object Name,Status
>>> Pause
>>> [PSCustomObject]@{Name='Some long name';Status='Some long status'} #1
>>> Pause
>>> [PSCustomObject]@{Name='Even longer name';Status='Even longer status'}
>>> Pause

Press Enter to continue...:
Name           Status
----           ------
Ethernet       Up
Some long name Some long status
Press Enter to continue...:
Even longer... Even longer s...
Press Enter to continue...:


PS>

隐式Format-Table 在第一个Pause 之前不打印任何内容(严格地说它打印空行),因为它仍在等待更多输入对象(尚未经过300 毫秒)来决定列宽。当第一个对象 (#1) 在 300 毫秒间隔后出现时(假设您按 Enter 不快),然后Format-Table 决定列宽并打印所有收集的对象。任何其他对象都将立即打印,但它们不再影响列宽。如果列的值太大,它将被截断。

PS> Get-NetAdapter | Select-Object Name,Status | Format-Table
>>> Pause

Name     Status
----     ------
Ethernet Up


Press Enter to continue...:
PS>

使用此代码,显式Format-Table 的结束块将在Pause 之前执行。在结束块Format-Table 知道它已经获得了所有输入,因此它可以决定列宽并立即输出所有收集的对象。隐式Out-Default 看到来自Format-Table 输出的格式化对象,Out-Default 知道它们不需要任何额外的格式化并立即在主机(控制台)上打印它们。所以整个表格在Pause 被调用之前就被打印出来了。

注意表格结束标记(两个空行)的位置差异。在第一个示例中,它放在最后一个 Pause 之后。这是因为隐式 Format-Table 仍然处于活动状态并且仍在等待,所以您将附加对象传递给它。仅当您的命令完全完成时Format-Table 确认输入结束和表格标记的输出结束。在第二个示例中,显式 Format-Table 在 Pause 之前完成,因此整个表(包括表尾标记)在 Pause 命令之前打印。

在以前版本的 PowerShell 中也可以注意到表格结束标记的位置差异。

【讨论】:

    【解决方案2】:

    Format-Table 与我无关,我只是在 PowerSell V 4.0 和 PowerShel V5.0 中尝试以下操作,问题可以重现:

    Get-Process |Select-Object -Property name ; pause
    

    转弯是:

    Get-Process |Select-Object -Property Name  |%{Write-host $_.name};pause
    

    这里再次暂停首先运行:

    Get-Process |%{$_.name | Set-Content 'c:\temp\test.txt';$_} |Select-Object -Property Name  ;pause
    

    这里没有

    Get-Process |%{$_.name | Set-Content 'c:\temp\test.txt';Start-Sleep -Milliseconds 1;$_} |Select-Object
    -Property Name  ;pause
    

    对我来说,在 PowerShell V5.0 中,所有的工作方式都类似于如果流水线指令中不需要主机,那么这些指令将异步运行。

    我希望像@Keith Hill 这样的人看看这种行为。

    【讨论】:

    • 所有内容都隐式传输到Out-Default,因此您的第一个代码等同于.{Get-Process |Select-Object -Property name ; pause}|Out-Default。而Out-Default 决定使用Format-Table 来格式化您的输出。所以实际上你有.{Get-Process |Select-Object -Property name ; pause}|Format-Table。如果将Format-Table 更改为Format-List 或显式设置列宽Format-Table @{e='Name';w=50},则输出后将调用pause。如果您在每个输出对象上添加计时,那么您会看到,在 300 毫秒间隔后开始输出。
    • .{1..20|ForEach-Object {$SW=[Diagnostics.Stopwatch]::StartNew()}{Write-Host "Before writing '$_' ($($SW.ElapsedMilliseconds))";[PSCustomObject]@{Property=$_};Write-Host "After writing '$_' ($($SW.ElapsedMilliseconds))";Start-Sleep -Milliseconds 20}}|Format-Table
    • 命令作为整个管道传输到单个 Out-Default 实例。您可以通过覆盖Out-Default 清楚地看到它。 function Out-Default{begin{Write-Host B}process{Write-Host P}end{Write-Host E}}。使用该命令1,2;3,4,您将看到 B、P、P、P、P、E。所以它是 .{1,2;3,4}|Out-Default 而不是 1,2|Out-Default;3,4|Out-Default。而且我并不是说内置的Out-Default总是选择使用Format-Table。我只说它为您的输出选择Format-Table(具有单个属性的自定义对象,格式文件中没有定义格式)。
    • .{Get-Process |Select-Object -Property Name |Format-Table;pause}|Out-Default: Format-Table仅应用于此Get-Process |Select-Object -Property Name。当Format-Table 的结束块被调用(并且这显然发生在下一个命令pause 调用之前)它输出任何持有对象。 Out-Default 查看格式化对象并选择不对它们使用任何附加格式并立即在控制台上打印它们。
    • .{Get-Process |Select-Object -Property Name ;pause}|Out-Default: Get-Process |Select-Object -Property Name 输出具有单一属性的自定义对象,格式文件中没有定义格式,Out-Default 选择在它们上使用Format-Table。 Format-Table 将对象保持 300 毫秒以决定列宽。 Get-Process|Select-Object -Property Name;pause;[PSCustomObject]@{Name='On that object column width for Name desided'};[PSCustomObject]@{Name='That string will be cutted as it does not fit column width for Name column'}
    猜你喜欢
    • 2012-10-10
    • 2011-11-22
    • 2016-08-14
    • 2010-12-04
    • 1970-01-01
    • 2022-11-23
    相关资源
    最近更新 更多