提问人:scriptkiddie 提问时间:4/29/2017 最后编辑:mklement0scriptkiddie 更新时间:9/14/2023 访问量:1219
PowerShell 输出在函数之间交叉
PowerShell output is crossing between functions
问:
我正在 Windows 10 上编写 5.1 版的 PowerShell 脚本,该脚本获取有关本地系统(以及最终其子网)的某些信息,并将它们输出到文本文件中。起初,我将所有方面都集中在一个函数中。我在输出和函数时遇到了输出问题,其中输出将被注入到 .getUsersAndGroups
getRunningProcesses
getUsersAndGroups
getRunningProcesses
这两个功能是:
# Powershell script to get various properties and output to a text file
Function getRunningProcesses()
{
# Running processes
Write-Host "Running Processes:
------------ START PROCESS LIST ------------
"
Get-Process | Select-Object name,fileversion,productversion,company
Write-Host "
------------- END PROCESS LIST -------------
"
}
Function getUsersAndGroups()
{
# Get Users and Groups
Write-Host "Users and Groups:"
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | Foreach-Object {
$groups = $_.Groups() | Foreach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
$_ | Select-Object @{n='Username';e={$_.Name}},@{n='Group';e={$groups -join ';'}}
}
}
getRunningProcesses
getUsersAndGroups
当我调用之后,输出如下所示(根本不输出):getUsersAndGroups
getRunningProcesses
getUsersAndGroups
Running Processes:
------------ START PROCESS LIST ------------
Name FileVersion ProductVersion Company
---- ----------- -------------- -------
armsvc
aswidsagenta
audiodg
AVGSvc
avgsvca
avguix 1.182.2.64574 1.182.2.64574 AVG Technologies CZ, s.r.o.
conhost 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
csrss
csrss
dasHost
dwm
explorer 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
hkcmd 8.15.10.2900 8.15.10.2900 Intel Corporation
Idle
igfxpers 8.15.10.2900 8.15.10.2900 Intel Corporation
lsass
MBAMService
mDNSResponder
Memory Compression
powershell_ise 10.0.14393.103 (rs1_release_inmarket.160819-1924) 10.0.14393.103 Microsoft Corporation
RuntimeBroker 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
SearchFilterHost
SearchIndexer
SearchProtocolHost
SearchUI 10.0.14393.953 (rs1_release_inmarket.170303-1614) 10.0.14393.953 Microsoft Corporation
services
ShellExperienceHost 10.0.14393.447 (rs1_release_inmarket.161102-0100) 10.0.14393.447 Microsoft Corporation
sihost 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
smss
spoolsv
sqlwriter
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
System
taskhostw 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
ToolbarUpdater
wininit
winlogon
WtuSystemSupport
WUDFHost
------------ END PROCESS LIST ------------
Users and Groups:
当我在输出注入之前调用时,更糟糕的是,根本没有列出正在运行的进程,而是列出了很多空行。getUsersAndGroups
getRunningProcesses
getUsersAndGroups
getRunningProcesses
如何分离或控制输出,使其在输出之前输出?getUsersAndGroups
getRunningProcesses
注入输出的输出如下所示:
Running Processes:
------------ START PROCESS LIST ------------
Username Group
-------- -----
Administrator Administrators
debug255 Administrators;Hyper-V Administrators;Performance Log Users
DefaultAccount System Managed Accounts Group
Guest Guests
------------ END PROCESS LIST ------------
非常感谢您的帮助!
答:
TL的;博士:
基础问题会影响 Windows PowerShell 和 PowerShell (Core) 7+,至少为 v7.3.4(在撰写本文时是最新的),并且由于它是设计行为的(不幸的)副作用,因此可能会也可能不会得到修复。
若要防止输出出现乱序,请通过显式调用 Format-Table 或 Out-Host
强制同步显示输出:
getUsersAndGroups | Format-Table
getRunningProcesses | Format-Table
两者都解决了主要的显示问题,但它们都是次优解决方案,因为它们都干扰了作为数据提供的输出:Format-Table
Out-Host
Format-Table
输出格式化指令而不是数据,这仅对 PowerShell 的显示输出格式化系统有意义,即当输出转到显示器或其中一个 cmdlet 时,特别是包括 和 因此也 .生成的格式不适合编程处理。Out-*
Out-File
>
Out-Host
完全不输出任何数据,直接打印到显示器上,无法捕获或重定向数据。
相关 GitHub 问题:
GitHub issue #4594:讨论一般令人惊讶的异步行为。
GitHub 问题 #13985:使用 CLI 时可能会丢失数据。
背景资料:
在 PowerShell 会话中:
这主要是一个显示问题,您不需要此解决方法来捕获变量中的输出、将其重定向到文件或通过管道传递它。
对于依赖显示输出按输出顺序显示的交互式脚本,您确实需要它,这主要包括确保在显示交互式提示之前打印相关信息;例如:
# !! Without Format-table, the prompt shows *first*. [pscustomobject] @{ foo = 1; bar = 2 } | Format-Table Read-Host 'Does the above look OK?'
从外部调用 PowerShell CLI(powershell -file ... 或 powershell -command ...
)时:
如果不使用
Out-Host
,可能会发生实际数据丢失,因为如果脚本/命令以 - 请参阅 GitHub issue #13985;例如:exit
# !! Prints only 'first' powershell.exe -command "'first'; [pscustomobject] @{ foo = 'bar' }; exit"
但是,与 PowerShell 会话内使用不同,
Format-Table
或Out-Host
修复了显示和数据捕获/重定向问题,因为即使 的输出也会发送到 stdout,如外部调用方所看到的那样(但请注意,PowerShell 的输出格式化系统生成的 for-display 表示形式通常不适合编程处理)。[1]Out-Host
注意:以上所有内容同样适用于 PowerShell (Core) 7+ 及其 pwsh
CLI,最高可达 v7.3.1。
在这种情况下,PowerShell 的问题行为的说明:
使用 MCVE(最小、完整和可验证示例)演示问题可能会有所帮助:
Write-Host "-- before"
[pscustomobject] @{ one = 1; two = 2; three = 3 }
Write-Host "-- after"
在 PSv5+ 中,这将产生:
-- before
-- after
one two three
--- --- -----
1 2 3
发生了什么事?
调用同步生成输出。
Write-Host
值得注意的是,它绕过了正常的成功输出流,并(实际上)直接写入控制台 - 大多数情况下,即使有合法用途,也应避免使用
Write-Host
。Write-Host
但是,请注意,即使是发送到成功输出流的输出对象也可以同步显示,并且经常显示,特别是作为基元 .NET 类型(如字符串和数字)实例的对象,以及其隐式输出格式导致非表格输出的对象,以及具有与其关联的显式格式数据的类型(见下文)。
隐式输出 - 由于未捕获语句的输出 - 出乎意料地不同步:
[pscustomobject] @{ one = 1; two = 2; three = 3 }
- 最初生成了一行空行。
- 所有实际输出都遵循最终调用。
Write-Host
这个有用的答案解释了为什么会发生这种情况;总之:
隐式输出的格式基于要输出的对象的类型;在手头的情况下,是隐式使用的。
Format-Table
在 Psv5+ 中,隐式应用的
Format-Table
现在最多等待 300 毫秒,以确定合适的列宽。但请注意,这仅适用于其类型表格式化指令未预定义的输出对象;如果是,则提前确定列宽,并且不会发生等待。
若要测试具有全名的给定类型是否具有与之关联的表格式数据,可以使用以下命令:
<FullTypeName>
# Outputs $true, if <FullTypeName> has predefined table-formatting data. Get-FormatData <FullTypeName> -PowerShellVersion $PSVersionTable.PSVersion | Where-Object { $_.FormatViewDefinition.Control.ForEach('GetType') -contains [System.Management.Automation.TableControl] }
不幸的是,这意味着后续命令在该时间窗口内执行,并可能产生不相关的输出(通过管道绕过输出命令,如
Write-Host
)或在格式表
输出开始之前提示用户输入。- 当从外部调用 PowerShell CLI 并在时间窗口内调用时,将有效丢弃所有挂起的输出(包括后续的同步输出)。
exit
- 当从外部调用 PowerShell CLI 并在时间窗口内调用时,将有效丢弃所有挂起的输出(包括后续的同步输出)。
GitHub 问题 #4594 中讨论了有问题的行为;虽然仍有解决方案的希望,但很长一段时间都没有活动。
注意:此答案最初错误地将 PSv5+ 300 毫秒延迟归咎于可能令人惊讶的标准输出格式行为(即,如果应用了表格式,则发送到管道的第一个对象决定了管道中所有对象的显示格式 - 请参阅此答案)。
[1] CLI 允许您使用 -OutputFormat
XML 以结构化文本格式请求输出,即基于 XML 的序列化格式(称为 CLIXML)。PowerShell 在后台使用这种格式跨进程序列化数据,外部程序通常不知道这种格式,这就是为什么在实践中很少使用 -OutputFormat Xml
的原因。请注意,当您使用它时,格式化表
/主机
解决方法将再次无效,因为原始输出对象已丢失。
评论
getRunningProcesses|ft
getUsersAndGroups|ft
ft
Format-Table
)Write-Hosts