【发布时间】:2020-05-15 13:36:25
【问题描述】:
是否有任何理智、可靠的合同规定在给定的 PowerShell 主机实现中是否支持 Write-Host,在可以针对任何合理的主机实现运行的脚本中?
(假设我了解Write-Host 和Write-Output/Write-Verbose 之间的区别,并且我肯定希望Write-Host 语义(如果支持的话)用于这个特定的人类可读文本。)
我曾想过尝试询问$Host 变量或$Host.UI/$Host.UI.RawUI,但我发现的唯一相关差异是:
-
在
$Host.Name:- Windows powershell.exe 命令行有
$Host.Name = 'ConsoleHost' - ISE 有
$Host.Name = 'Windows PowerShell ISE Host' - SQL Server 代理作业步骤有
$Host.Name = 'Default Host' - 我没有安装任何非 Windows 版本,但我希望它们有所不同
- Windows powershell.exe 命令行有
-
在
$Host.UI.RawUI:- Windows powershell.exe 命令行返回
$Host.UI.RawUI的所有属性的值 - ISE 不为
$Host.UI.RawUI的某些属性返回值(或$null),例如$Host.UI.RawUI.CursorSize - SQL Server 代理作业步骤未返回所有
$Host.UI.RawUI的值 - 同样,我无法在任何其他平台上签到
- Windows powershell.exe 命令行返回
维护支持Write-Host 的$Host.Name 值列表似乎有点负担,尤其是现在PowerShell 是跨平台的。我有理由希望能够从任何主机调用该脚本,并且只需做正确的事情。
背景
我编写了一个脚本,可以从 PowerShell 命令提示符、ISE 或 SQL Server 代理作业中合理运行。该脚本的输出完全是文本的,供人类阅读。从命令提示符或 ISE 运行时,输出使用 Write-Host 着色。
可以通过两种不同的方式设置 SQL Server 作业,并且都支持将输出捕获到 SQL Server 代理日志查看器中:
-
通过 CmdExec 步骤,这是简单的命令行执行,其中 Job Step 命令文本是可执行文件及其参数,因此您调用 powershell.exe 可执行文件。捕获的输出是进程的stdout/sterr:
powershell.exe -Command x:\pathto\script.ps1 -Arg1 -Arg2 -Etc -
通过 PowerShell 步骤,其中 Job Step 命令文本是由其自己的嵌入式 PowerShell 主机实现解释的原始 PS 脚本。捕获的输出是通过
Write-Output或Write-Error写入的任何内容:#whatever Do-WhateverPowershellCommandYouWant x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
由于 SQL Server 主机实现的其他一些缺陷,我发现您可以使用 Write-Output 或 Write-Error 发出输出,但不能同时使用两者。如果作业步骤失败(即如果您throw 或Write-Error 'foo' -EA 'Stop'),您仅 会在日志中获得错误流,如果成功,您只会在日志中获得输出流。
此外,嵌入式 PS 实现不支持Write-Host。至少在 SQL Server 2016 之前,Write-Host 会抛出 System.Management.Automation.Host.HostException 并带有消息A command that prompt the user failed because the host program or the command type does not support user interaction。
为了支持我的所有用例,到目前为止,我开始使用自定义函数 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()) }
}
}
从 SQL Server 2019(可能更早)开始,Write-Host 似乎不再在嵌入式 SQL Server 代理 PS 主机中引发异常,而是一个不向输出或错误流发出任何内容的空操作。由于没有例外,我的脚本的Write-Message 函数无法再可靠地检测它应该使用Write-Host 还是StringBuilder.AppendLine。
SQL Server 代理作业的基本解决方法是使用更成熟的 CmdExec 步骤类型(其中 Write-Output 和 Write-Host 都被捕获为标准输出),但我更喜欢 PowerShell 步骤类型(除其他原因外) ) 它能够可靠地将命令拆分为多行,所以我很想看看是否有更全面的、基于 PowerShell 的方法来解决Write-Host 是否对我所在的主机有用的问题。
【问题讨论】:
-
PowerShell 输出着色感觉像是一个被添加的功能,然后被忽略了。我能想到的变通方法(不是解决方案):1)使用
$Host.Name检测支持Write-Host的已知场景,其他情况默认为Write-Output; 2)使用开关指定是否使用Write-Host和coloration,依赖调用者指定模式 -
@Tydaeus :: 事实上,到目前为止,Switch 参数是我遇到的唯一可靠的方法。现在,在我的大部分脚本中,我已将
$script:can_write_host = $true替换为$script:can_write_host = (-not $BufferOutput),其中 BufferOutput 是 Switch 参数。 -
Jeffrey Snover 早在 2013 年就告诉我们不要一起使用 Write-Host :) Write-Host Considered Harmful
-
@Dennis :: 直到存在可行的彩色、人类可读、非流水线输出的替代方案,我怀疑大多数人不会关心
-
好吧,如果您想要非流水线输出,那么 Write-Host 就是适合您的 cmdlet...但是一旦您开始编写高级函数,那就不可能了。
标签: powershell write-host powershell-hosting