简体   繁体   English

如何确定 Write-Host 是否适用于当前主机

[英]How to determine if Write-Host will work for the current host

Is there any sane, reliable contract that dictates whether Write-Host is supported in a given PowerShell host implementation, in a script that could be run against any reasonable host implementation?在可以针对任何合理的主机实现运行的脚本中,是否有任何理智、可靠的合同规定在给定的 PowerShell 主机实现中是否支持Write-Host

(Assume that I understand the difference between Write-Host and Write-Output / Write-Verbose and that I definitely do want Write-Host semantics, if supported, for this specific human-readable text.) (假设我了解Write-HostWrite-Output / Write-Verbose之间的区别,并且如果支持,对于这个特定的人类可读文本,我肯定想要Write-Host语义。)

I thought about trying to interrogate the $Host variable, or $Host.UI / $Host.UI.RawUI but the only pertinent differences I am spotting are:我想过尝试询问$Host变量或$Host.UI / $Host.UI.RawUI但我发现的唯一相关差异是:

  • in $Host.Name :$Host.Name

    • The Windows powershell.exe commandline has $Host.Name = 'ConsoleHost' Windows powershell.exe 命令行具有$Host.Name = 'ConsoleHost'
    • ISE has $Host.Name = 'Windows PowerShell ISE Host' ISE 有$Host.Name = 'Windows PowerShell ISE Host'
    • SQL Server Agent job steps have $Host.Name = 'Default Host' SQL 服务器代理作业步骤具有$Host.Name = 'Default Host'
    • I have none of the non-Windows versions installed, but I expect they are different我没有安装任何非 Windows 版本,但我希望它们有所不同
  • in $Host.UI.RawUI :$Host.UI.RawUI

    • The Windows powershell.exe commandline returns values for all properties of $Host.UI.RawUI Windows powershell.exe 命令行返回$Host.UI.RawUI的所有属性的值
    • ISE returns no value (or $null ) for some properties of $Host.UI.RawUI , eg $Host.UI.RawUI.CursorSize对于$Host.UI.RawUI的某些属性,ISE 不返回任何值(或$null ),例如$Host.UI.RawUI.CursorSize
    • SQL Server Agent job steps return no values for all of $Host.UI.RawUI SQL 服务器代理作业步骤不返回所有$Host.UI.RawUI的值
    • Again, I can't check in any of the other platforms同样,我无法签入任何其他平台

Maintaining a list of $Host.Name values that support Write-Host seems like it would be bit of a burden, especially with PowerShell being cross-platform now.维护支持Write-Host$Host.Name值列表似乎有点负担,尤其是 PowerShell 现在是跨平台的。 I would reasonably want the script to be able to be called from any host and just do the right thing .我会合理地希望脚本能够从任何主机调用并且只是做正确的事情

Background背景

I have written a script that can be reasonably run from within the PowerShell command prompt, from within the ISE or from within a SQL Server Agent job.我编写了一个脚本,可以从 PowerShell 命令提示符、ISE 或 SQL 服务器代理作业中合理运行。 The output of this script is entirely textual, for human reading.此脚本的 output 完全是文本,供人类阅读。 When run from the command prompt or ISE, the output is colorized using Write-Host .从命令提示符或 ISE 运行时,output 使用Write-Host着色。

SQL Server jobs can be set up in two different ways, and both support capturing the output into the SQL Server Agent log viewer: SQL 服务器作业可以通过两种不同的方式设置,并且都支持将 output 捕获到 SQL 服务器代理日志查看器中:

  1. via a CmdExec step, which is simple command-line execution, where the Job Step command text is an executable and its arguments, so you invoke the powershell.exe executable.通过 CmdExec 步骤,这是简单的命令行执行,其中 Job Step 命令文本是可执行文件及其 arguments,因此您调用 powershell.exe 可执行文件。 Captured output is the stdout/sterr of the process:捕获的 output 是该过程的 stdout/sterr:

     powershell.exe -Command x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
  2. via a PowerShell step, where the Job Step command text is raw PS script interpreted by its own embedded PowerShell host implementation.通过 PowerShell 步骤,其中作业步骤命令文本是由其自己的嵌入式 PowerShell 主机实现解释的原始 PS 脚本。 Captured output is whatever is written via Write-Output or Write-Error :捕获的 output 是通过Write-OutputWrite-Error内容:

     #whatever Do-WhateverPowershellCommandYouWant x:\pathto\script.ps1 -Arg1 -Arg2 -Etc

Due to some other foibles of the SQL Server host implementation, I find that you can emit output using either Write-Output or Write-Error , but not both.由于 SQL 服务器主机实现的其他一些缺点,我发现您可以使用Write-OutputWrite-Error发出 output ,但不能同时使用。 If the job step fails (ie if you throw or Write-Error 'foo' -EA 'Stop' ), you only get the error stream in the log and, if it succeeds, you only get the output stream in the log.如果作业步骤失败(即如果您throwWrite-Error 'foo' -EA 'Stop' ),您只会在日志中收到错误 stream ,如果成功,您只会在日志中收到 output ZF7B44CFAFD5C5222E 错误。

Additionally, the embedded PS implementation does not support Write-Host .此外,嵌入式 PS 实现不支持Write-Host Up to at least SQL Server 2016, Write-Host throws a System.Management.Automation.Host.HostException with the message A command that prompts the user failed because the host program or the command type does not support user interaction .至少在 SQL Server 2016 之前, Write-Host抛出System.Management.Automation.Host.HostException并带有消息A command that prompt the user failed because the host program or command type does not support user interaction

To support all of my use-cases, so far, I took to using a custom function Write-Message which was essentially set up like (simplified):为了支持我的所有用例,到目前为止,我开始使用自定义 function Write-Message ,它基本上设置为(简化):

 $script:can_write_host = $true
 $script:has_errors = $false
 $script:message_stream = New-Object Text.StringBuilder

 function Write-Message {
     Param($message, [Switch]$iserror)

     if ($script:can_write_host) {
         $private:color = if ($iserror) { 'Red' } else { 'White' }
         try { Write-Host $message -ForegroundColor $private:color }
         catch [Management.Automation.Host.HostException] { $script:can_write_host = $false }
     }
     if (-not $script:can_write_host) {
         $script:message_stream.AppendLine($message) | Out-Null
     }
     if ($iserror) { $script:has_errors = $true }
 }

 try { 
     <# MAIN SCRIPT BODY RUNS HERE #>
 }
 catch { 
     Write-Message -Message ("Unhandled error: " + ($_ | Format-List | Out-String)) -IsError
 }
 finally {
     if (-not $script:can_write_host) {
         if ($script:has_errors) { Write-Error ($script:message_stream.ToString()) -EA 'Stop' }
         else { Write-Output ($script:message_stream.ToString()) }
     }
 } 

As of SQL Server 2019 (perhaps earlier), it appears Write-Host no longer throws an exception in the embedded SQL Server Agent PS host, but is instead a no-op that emits nothing to either output or error streams. As of SQL Server 2019 (perhaps earlier), it appears Write-Host no longer throws an exception in the embedded SQL Server Agent PS host, but is instead a no-op that emits nothing to either output or error streams. Since there is no exception, my script's Write-Message function can no longer reliably detect whether it should use Write-Host or StringBuilder.AppendLine .由于没有例外,我的脚本的Write-Message function 无法再可靠地检测它是否应该使用Write-HostStringBuilder.AppendLine

The basic workaround for SQL Server Agent jobs is to use the more-mature CmdExec step type (where Write-Output and Write-Host both get captured as stdout), but I do prefer the PowerShell step type for (among other reasons) its ability to split the command reliably across multiple lines, so I am keen to see if there is a more-holistic, PowerShell-based approach to solve the problem of whether Write-Host does anything useful for the host I am in. SQL 服务器代理作业的基本解决方法是使用更成熟的 CmdExec 步骤类型(其中Write-OutputWrite-Host都被捕获为标准输出),但我更喜欢 PowerShell 步骤类型,因为(除其他原因外)它的能力将命令可靠地拆分为多行,因此我很想看看是否有更全面的、基于 PowerShell 的方法来解决Write-Host是否对我所在的主机有用的问题。

Just check if your host is UserInteractive or an service type environment.只需检查您的主机是 UserInteractive 还是服务类型环境。

$script:can_write_host = [Environment]::UserInteractive

Another way to track the output of a script in real time is to push that output to a log file and then monitor it in real time using trace32.另一种实时跟踪脚本的 output 的方法是将 output 推送到日志文件,然后使用 trace32 对其进行实时监控。 This is just a workaround, but it might work out for you.这只是一种解决方法,但它可能对您有用。

Add-Content -Path "C:\Users\username\Documents\PS_log.log" -Value $variablewithvalue

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM