在某些情况下,如果我在Select-Object
命令后尝试暂停或休眠,则在命令之前发生暂停/休眠。
例如,使用
Get-NetAdapter | Select-Object Name,Status
Pause
或
Get-NetAdapter | Select-Object Name,Status | Where-Object {$_ -ne $null}
Pause
输出是:
Press Enter to continue...: Name Status ---- ------ Wi-Fi Up Ethernet Disconnected
而
Get-NetAdapter | Select-Object Name,Status | Format-Table
Pause
输出是:
Name Status ---- ------ Wi-Fi Up Ethernet Disconnected Press Enter to continue...:
这里发生了什么?这是一个错误还是一个功能?
答案 0 :(得分:6)
您看到的是新PowerShell v5功能的后果。 Format-Table
现在收集输入300毫秒以找到更好的列宽。即使您明确指定-AutoSize:$false
,它也会以这种方式工作。
在命令提示符下键入command时,该命令隐式通过管道传递给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版本中注意到。
答案 1 :(得分:1)
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的人能够看到这种行为。