【问题标题】:Is there a way to set a variable up to place output to stdout or null?有没有办法设置一个变量以将输出放置到标准输出或空值?
【发布时间】:2019-10-29 15:48:45
【问题描述】:

我想在我的代码中设置一个变量,最终定义我是否会看到一些输出。

  • "hello" 写入标准输出
  • "hello" > $null 抑制输出

我的想法是这样的:

$debugOutputSwitch = $true
$outputVar = $null
if ($debugOutputSwitch){ $outputVar = **STDOUT** }
...
Write-Host "Something I want out anyway"
"Something I might not want on STDOUT" > $outputVar

如果这个总体思路是可行的,那么STDOUT 就是我要找的

如果这个想法是完全错误的......好吧......那我就迷路了

【问题讨论】:

  • 为什么不直接使用Write-Debug 并设置$DebugPreference? (Continue 输出,SilentlyContinue 不输出。)
  • 听起来更像你想要的Write-Verbose,但@JeroenMostert 的观点是:)
  • 请您提供一个例子好吗?

标签: powershell null stdout


【解决方案1】:

您想阅读的是 Powershell 中的 output streams and redirection。这包括有关所有不同输出流的信息以及如何使用内置结构控制它们的相关性。就像有 Write-HostWrite-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 写入VerboseDebug 流不是一个选项,您仍然可以将输出重定向到@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-HostWrite-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-WarningWrite-VerboseWrite-ErrorWrite-Debug cmdlet。

【讨论】:

  • 那么你的建议是每次我需要切换时修改代码中的每个输出?好吧,我希望我误解了。
  • 也许,只是也许我重新发明了一辆三轮车,而您建议使用轮子。只是也许。但无论如何,谢谢你,你无缘无故地在这里发布的那篇学士论文,你大肆宣传我要解决它。我是认真的。谢谢
  • 这是一个很好的类比。您的理论方法没有错,但这是一个已经通过第一方解决方案解决的问题。我确实深入研究了一些事情,但我认为您要为您的案例使用的是Write-Verbose,它由-Verbose 参数控制(如前所述,您需要将[CmdletBinding()] 属性添加到函数或脚本定义)或设置$VerbosePreference)。
  • 嗨。我完全不记得了,我当时想解决什么。但是我问的是如何抑制输出,而你告诉我不要发送它。那么我的问题的答案是什么?做不到?
  • 不,它可以做到,有时你想重定向输出。上面的答案解释了如何处理重定向以及如何使用重定向和不同的Write- cmdlet 来写入每个不同的流。但是,您在问题中描述用例的方式是您试图重新实现 PowerShell 中已经存在的日志记录框架。但是,如果你只是想抑制命令的输出,你可以重定向到$null (> $null),管道到Out-Null (| Out-Null),或者转换到[void] ([void]( Some-Command ))。
【解决方案2】:

好的。 感谢这里所有的聪明人的动力。 这个答案可能不是最好的解决方法,但它确实有效!

要实现这一点,您需要了解两件事:

  1. 如果你习惯用Write-Host,那就不行了,你必须用Write-Output
  2. 您可能需要学习使用脚本块作为函数参数。

一个是不言自明的,所以这里是如何实现#2:

Function Test-SctiptBlockParam {
  Param(
    $scriptblock
  )
  if ($debugOutput) {
    Invoke-Command $scriptblock
  } else {
    (Invoke-Command $scriptblock) > $null
  }
}

Test-SctiptBlockParam -scriptblock { Write-Output "I want to see on the STDOUT sometimes" }

最后,这是我的outputcode的例子

【讨论】:

  • 你不需要像这样使用脚本块。将$debugOutput = $true 替换为$VerbosePreference = 'Continue',只需调用Write-Verbose 而不是Write-Output。也就是说,如果您想改进代码以重定向所有流,而不仅仅是将成功流重定向到 $null,请将 > 替换为 *> 以重定向 all 输出流到$null。那么在脚本块中使用哪个Write-Output cmdlet 并不重要。
  • 好吧,我不明白。我拿走了我的开关: $debugOutput = $true 有两种状态打开和关闭我用 $VerbosePreference = 'Continue' 替换它是我的开关?在哪个州?另一个州是什么样子的?
  • 与此同时,我发现了一个我的设计不起作用的特定案例。如果您以这种方式调用外部脚本,在其中调用 write-output:a) 从脚本主体或核心或其他任何内容,但不是从函数中调用 - 它有效 b) 从函数中调用 - 它不起作用:(
  • 我已经用一个关于如何使用内置输出流 cmdlet 的示例更新了我的答案,可选择编写详细或触发调试断点。
  • 查看您的更新。这需要我消化一下才能弄清楚所有的含义。它看起来非常好,但我想抑制其他人脚本的输出。我也许可以向他们解释使用 write-output 而不是 write-host。但是我永远无法向他们解释在不同的情况下使用不同的方法:(但我可能会误解并会调查它。
【解决方案3】:

再次可能不是最好的方法,但有效。

@Bender the Greatest 已经提到了一个带有 [CmdletBinding] 的函数应该有助于达到目的。

我将尝试提供一个非常基本的示例 -

像这样的选择性记录器功能 -

function Selective-Log {
 [CmdletBinding()]
 param(
     [Parameter()]
     [ValidateNotNullOrEmpty()]
     [string]$Message,

     [Parameter()]
     [ValidateNotNullOrEmpty()]
     [ValidateSet('Info','Warning','Error')]
     [string]$Severity = 'Info'
 )

 if($Severity -eq 'Info'){
 Write-Output $Message > $null
 }else{
 Write-Output $Message 
 }}

现在当你在你的脚本中使用它时

Selective-Log -Message Test1 -Severity Info

它不会记录任何东西

然后,如果您想记录,您可以选择 Info 以外的严重性

Selective-Log -Message Test2 -Severity Error

你也可以像Selective-Log Test2 Error一样使用它

希望这对某人有所帮助

【讨论】:

    猜你喜欢
    • 2014-05-31
    • 2019-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-04
    相关资源
    最近更新 更多