您想阅读的是 Powershell 中的 output streams and redirection。这包括有关所有不同输出流的信息以及如何使用内置结构控制它们的相关性。就像有 Write-Host 和 Write-Output cmdlet 一样,还有其他几个可以控制写入哪个流。
关于输出流
总共有 6 个流。记下它们的编号,因为这些流标识符用于控制要重定向的流:
-
1 - 成功流 - 当通过 Powershell Pipeline 传递信息时使用此流。这是“默认”流,但也可以使用
Write-Output 写入。
-
2 - 错误流 - 应将错误写入此流。可以使用
Write-Error 写信,并附上更多错误信息。
-
3 - 警告流 - 用于写入警告信息。可以写信给
Write-Warning。
-
4 - 详细流 - 用于编写详细输出。默认情况下不显示,但可以通过设置
$VerbosePreference = "Continue" 或在函数或脚本上使用[CmdletBinding()] 属性并传入-Verbose 标志来显示。使用 Write-Verbose 写入详细流。
-
5 - 调试流 - 用于写入调试流,并可选择触发断点。默认情况下不显示或触发断点,但可以使用
$DebugPreference 变量控制,或者通过在脚本或函数上使用[CmdletBinding()] 属性并使用-Debug 标志来控制。您可以使用 Write-Debug cmdlet 写入调试流。
-
6 - 信息流 - 可由
Write-Host 写入。这是控制台主机输出,不是管道的一部分。
重定向流
您也可以使用重定向运算符将其他流重定向到成功流。上面的每个流都有一个与之关联的数字。这是每个流的数字表示。
重定向操作符如下:
-
> - 将成功流重定向到文件(覆盖)
-
#> - 将# 流重定向到文件(例如2> somefile.txt)
-
>> - 将成功流重定向到文件(附加,您也可以使用带编号的流,如覆盖文件运算符)
-
>&1 - 将 any 流重定向到 success 流(注意,与其他重定向运算符不同,您可以仅 重定向 到 成功流。使用其他流标识符将导致错误)。
另请注意,您可以使用* 代替流编号,这将同时重定向所有流。
以下是一些将输出从一个流重定向到另一个流的示例(如果您熟悉它,它有点 UNIX-y):
# Write success stream to file
Write-Output "Here is some text for a file" > .\somefile.txt
# Write error stream to file (you have to first
Write-Error "Some error occurred" 2> .\somefile.txt
# Redirect all error output to the success stream
$myErrorOutput = Write-Error "My error output" 2>&1
# Append all script output streams to a single file
Get-OutputFromAllStreams.ps1 *>> somefile.txt
同时输出到文件和管道
您也可以同时将输出流重定向到文件和管道,using the Tee-Object cmdlet。这也适用于变量:
$myString = "My Output" | Tee-Object -FilePath .\somefile.txt
$myString2 = "My Output 2" | Tee-Object -Variable varName
展示如何使用不同的Write- cmdlet 的示例函数
注意以下函数是如何用[CmdletBinding()] 属性修饰的。这是使-Verbose 和-Debug 开关工作而无需您自己定义它们的关键。
function Write-DifferentOutputs {
[CmdletBinding()]
# These all visible by default but only the output stream is passed down the pipeline
Write-Output "Output stream"
Write-Warning "Warning stream"
Write-Error "Error stream"
Write-Host "Information stream"
# These are not visible by default, but are written when the `-Verbose` or `-Debug` flags are passed
# You can also manually set the $VerbosePreference or $DebugPreference variables to control this without parameters
Write-Verbose "Verbose stream"
Write-Debug "Debug stream"
}
使用-Verbose 或-Debug 开关调用上述函数以查看行为有何不同,也可以不使用任何标志调用它。
如果您确实需要,将输出重定向到 $null
如果有您不想看到的输出或出于其他原因使用Write- cmdlet 写入Verbose 或Debug 流不是一个选项,您仍然可以将输出重定向到@987654367 @ 或使用 Out-Null cmdlet。回想一下这个答案顶部的编号流,它们将在此处引用:
使用重定向
# Don't forget that *> redirects ALL streams, and may be what you want
Write-Output 'Success Stream' > $null
Write-Error 'Error Stream' 2> $null
Write-Warning 'Warning Stream' 3> $null
Write-Verbose 'Verbose Stream' 4> $null
Write-Debug 'Debug Stream' 5> $null
Write-Host 'Information Stream (yes you can suppress/redirect me)' 6> $null
使用Out-Null
请记住,您可以通过将输出重定向到 &1 来将其他流重定向到成功流。
# Remember, to pass information on the pipeline
# it MUST be on the success stream first
# Don't forget that *> redirects ALL streams, and may be what you want
Write-Output 'Success Stream' | Out-Null
Write-Error 'Error Stream' 2>&1 | Out-Null
Write-Warning 'Warning Stream' 3>&1 | Out-Null
Write-Verbose 'Verbose Stream' 4>&1 | Out-Null
Write-Debug 'Debug Stream' 5>&1 | Out-Null
Write-Host 'Information Stream (yes you can suppress/redirect me)' 6>&1 | Out-Null
当使用Out-Host 是合适的(“不要越过溪流”)
警告:与Write-Host不同,Out-Host 不会输出到信息流。相反,它直接输出到主机控制台。这使得直接写入Out-Host 的任何内容都无法重定向,除非使用Start-Transcript 或使用自定义PowerShell 主机。请注意,写入控制台主机的信息仍然对可能正在监视 PowerShell 输出的外部应用程序可见,因为最终即使 Out-Host 输出也会将其发送到 STDOUT。
称自己为Out-Host通常是多余的。默认情况下,PowerShell 通过Out-Default cmdlet(您应该永远直接调用Out-Default)在成功流上发送所有未分配的输出。也就是说,Out-Host 的一个有用调用是将格式化的对象数据同步输出到控制台:
注意:您也可以将来自其他输出流的信息重定向并输出到Out-Host,但没有理由这样做。对象数据只会在成功流上保持不变,其他流将在重定向之前首先将对象转换为其ToString() 表示。这也是为什么在这种情况下通过管道将对象传递给Out-Host 比Write-Host 更可取。
Get-Process msedge | Out-Host
不同输出流的一个警告是流之间没有同步性。 通常这不是问题,因为 PowerShell 会按顺序逐行执行指令,除了Write-Output 成功流之外,其他流都不是问题。但是,许多类型将具有计算的 for-display 属性,该属性是在将信息发送到 Out-Default 之前从脚本执行中异步计算的。
这可能导致显示的对象数据与写入控制台主机的其他输出流混合。在某些情况下,这甚至会导致写入控制台的信息丢失。 “穿越流”,如果你愿意的话,因为它与渲染输出的外观有关。
考虑以下示例和输出。这并没有展示流的混合,但考虑一下如果 Write-Host "end `n" 写在表格中间,您将在外部解析输出时遇到的麻烦:
Write-Host "start `n"
Get-LocalUser
Write-Host "end `n"
还有输出:
start
end
Name Enabled Description
---- ------- -----------
Administrator True
DefaultAccount False A user account managed by the system.
Disabled False Built-in account for guest access to the computer/domain
这对于定义表格格式的类型尤其有问题,该格式必须在将格式化数据发送到Out-Host进行显示之前计算列宽。预先定义表格宽度或根本不将输出格式化为表格的类型不存在此问题。 Out-Default 最多可能需要 300ms 来计算列宽。
当Out-Host作为管道的一部分被显式调用时,会跳过这些对象的表宽度计算,因为对象数据永远不会到达Out-Default。这主要用于确保对象数据打算以正确的顺序写入控制台。缺点是表格列可能不够宽,无法容纳每行的所有数据。
说了这么多,如果一定要处理一个脚本的控制台输出,建议把要处理的数据格式化成字符串,改用Write-Host,或者用其他方法获取数据到适合的地方外部处理。 for-display 格式不适用于外部处理。
@mklement1's answer here 如果您有兴趣了解有关此问题的更多信息,请进一步了解有关此问题的详细信息。
将整个命令输出重定向到Write- cmdlet
您可以轻松地将命令或 cmdlet 的所有输出通过管道传输到 Write- cmdlet 之一。我将在下面的示例中使用前面提供的Write-DifferentOutputs,但这适用于您运行的任何 cmdlet、脚本或命令:
Write-DifferentOutputs *>&1 | Write-Verbose
以上内容仅在$VerbosePreference = $Continue 或您将-Verbose 作为参数传递给脚本或函数时显示命令输出。
总结
在您最初的问题中,您正试图重新发明一个 Powershell 已经很好地支持的轮子。我建议您学习如何为每个流使用不同的Write-Output cmdlet,尤其是学习如何使用Write-Warning、Write-Verbose、Write-Error 和Write-Debug cmdlet。