powershell 与Write-Host相比,Write-Output的使用非常不可靠

zte4gxcn  于 2023-01-17  发布在  Shell
关注(0)|答案(1)|浏览(164)

有人向我提出了一个问题,如果我希望命令按顺序操作,建议使用Write-Output而不是Write-Host(因为Write-Host不会将输出放在管道上,而其他命令会这样做,这意味着Write-Host输出可能发生在管道上的其他命令之前或之后,从而导致输出非常混乱):command execution ordering inside a PowerShell scriptblock
遵循这个建议,我使用Write-Output做了一个简单的函数来模仿Write-Host的颜色语法。对于排序,这很好用,所以命令的输出现在是连续的,但是Write-Output的颜色输出现在很糟糕,所以如果我使用任何BackgroundColor,结果以非常丑陋的方式喷在屏幕上。Write-Host在彩色输出方面紧密可靠,并且没有“t出血到其他部分的控制台,所以使用写输出与颜色使一些真正丑陋/笨重的控制台输出。
我需要在离开这个函数之前以某种方式重置$host.ui吗?或者有人能建议一种方法来修改这个函数,使颜色保持在需要它们的区域,而不会渗到其他控制台区域吗?

function Write-Color ($text, $ForegroundColor, $BackgroundColor) {
    $defaultFore = $host.ui.RawUI.ForegroundColor
    $defaultBack = $host.ui.RawUI.BackgroundColor
    if ($ForegroundColor -ne $null) { $host.ui.RawUI.ForegroundColor = $ForegroundColor }
    if ($BackgroundColor -ne $null) { $host.ui.RawUI.BackgroundColor = $BackgroundColor }
    Write-Output $text
    $host.ui.RawUI.ForegroundColor = $defaultFore
    $host.ui.RawUI.BackgroundColor = $defaultBack
}

例如:

Write-Color "The dog sat on the couch" -ForegroundColor Red -BackgroundColor White
nqwrtyyt

nqwrtyyt1#

Write-Host * 是 * 生成(可能是彩色的)for-display 输出的正确工具-而不是通过PowerShell的 *success输出流 *、cmdlet调用和表达式(可选地通过显式Write-Output调用,但很少需要)输出 * 数据
This answer解释了如果您 * 混合 * Write-Host和success-stream输出,在PowerShell v5+中,打印到控制台的内容可能会出现 * 乱序

这是隐式应用的“表格”格式设置的“副作用”,它在打印输出之前收集一些数据,以便确定合适的列宽。这种情况只发生在(a)没有预定义格式数据,(b)具有4个或更少属性的输出类型(因为具有更多属性的类型默认为“列表”格式设置)。
GitHub issue #4594中讨论了有问题的行为;虽然仍有希望找到解决办法,但很长一段时间没有任何活动。

**对于此问题,没有好的解决方案(从PowerShell 7.0开始):

两个次优的变通方法:

  • (a)将触发异步行为的各个命令传输到... | Out-Host
  • 例如,在以下命令中,必须将带有Select-Object调用的命令发送到Out-Host,以便正确显示在屏幕上的两个Write-Host调用 * 之间 *:
Write-Host '------- 1'
Get-Item . | Select-Object FullName | Out-Host
Write-Host '------- 2'

*缺点:使用Out-Host意味着您将无法捕获或重定向命令输出,因为它会直接发送到 * 主机 *(显示器)。此外,(a)了解哪些命令会触发问题,(b)记得对每个命令应用解决方法也很麻烦。

  • (B)Write-Host调用替换为向成功输出流 * 发送具有嵌入的VT (Virtual Terminal) escape sequences(用于着色) 的字符串。*
  • 注意:需要Windows 10上的Windows PowerShell v5.1或PowerShell [核心] v6+
    *缺点:(彩色的)字符串成为代码的 * 数据输出 * 的一部分,因此在捕获/重定向输出时 * 被包括在内 *。
# Windows PowerShell 5.1: [char] 0x1b produces an ESC char.
    $green = [char] 0x1b + '[32m'; $reset = [char] 0x1b + '[m'
    # Print "green" in green.
    "It ain't easy being ${green}green${reset}."

    # PowerShell 6+: `e can be used inside "..." for ESC.
    $yellow = "`e[33m"; $reset = "`e[m"
    # Print "yellow" in yellow.
    "They call me mellow ${yellow}yellow${reset}."
  • 这些字符串包含ESC字符,这一事实实际上可以用来从数据流中过滤出显示字符串(假设实际数据不包含ESC字符),沿着... | Where-Object { -not ($_ -is [string] -and $_ -match '\e') }

嵌入VT转义序列允许您选择性地为字符串的 * 部分 * 着色。
Write-Host实现同样的效果需要用-NoNewline进行 * 多次 * 调用。
第三方cmdlet(模块)Write-ColoredOutput模拟Write-Host的语法,并使用[console]类型的属性打开和关闭着色,同时将字符串发送到 success output stream
这对于用给定的颜色编写 * 整个 * 字符串非常有效,但是您不能将不同颜色的部分拼凑在 * 一行 * 上,因为每个单独写入success输出流的字符串总是在其自己的行 * 上打印 *。
如果您想要一个方便的 Package 器来将VT序列直接嵌入到字符串中,您可以修改Write-HostColored函数from this answer,方法是将幕后发生的Write-Host调用替换为VT序列。

相关问题