Powershell 与写主机相比,写输出的使用非常不可靠

Powershell 与写主机相比,写输出的使用非常不可靠,powershell,colors,console,Powershell,Colors,Console,有人向我提出了一个问题,建议如果我希望命令按顺序运行,就在写主机上使用写输出(因为写主机不会将输出放在管道上,而其他命令会将输出放在管道上,这意味着写主机输出可以在管道上的其他命令之前或之后发生,从而导致非常混乱的输出): 按照这个建议,我使用Write-Output创建了一个简单的函数来模拟Write-Host的颜色语法。对于排序,这很有效,因此命令的输出现在是顺序的,但是颜色输出现在与写入输出非常糟糕,因此如果我使用任何背景颜色,结果会以非常丑陋的方式喷洒在屏幕上。写入主机的颜色输出紧凑可靠

有人向我提出了一个问题,建议如果我希望命令按顺序运行,就在写主机上使用写输出(因为写主机不会将输出放在管道上,而其他命令会将输出放在管道上,这意味着写主机输出可以在管道上的其他命令之前或之后发生,从而导致非常混乱的输出):

按照这个建议,我使用Write-Output创建了一个简单的函数来模拟Write-Host的颜色语法。对于排序,这很有效,因此命令的输出现在是顺序的,但是颜色输出现在与写入输出非常糟糕,因此如果我使用任何背景颜色,结果会以非常丑陋的方式喷洒在屏幕上。写入主机的颜色输出紧凑可靠,并且不会渗入控制台的其他部分,因此使用带有颜色的写入输出会导致一些非常难看/笨重的控制台输出

在离开该功能之前,我是否需要以某种方式重置$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
}
e、 g

Write Host
是生成(可能是彩色的)显示输出的正确工具,而不是通过PowerShell的成功输出流、cmdlet调用和表达式(可选通过显式
Write output
调用)输出数据,但这很少需要

说明如果混合使用
写入主机
和成功流输出,在PowerShell v5+中,打印到控制台的内容可以以不同的顺序出现

# 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}."
这是隐式应用表格格式的一个副作用,在异步的情况下,为了在打印输出之前收集一些数据,以便确定合适的列宽。仅当输出类型(a)没有预定义的格式数据,并且(b)具有4个或更少的属性(因为具有更多属性的类型默认为列表格式)时,才会发生这种情况

讨论了问题行为;虽然仍有希望找到解决办法,但很长一段时间以来没有任何活动

从PowerShell 7.0开始,此问题没有很好的解决方案

有两个次优的解决办法:

  • (a) 将触发异步行为的单个命令管道化到
    。|输出主机

    • 例如,在以下命令中,必须将带有
      选择对象
      调用的命令发送到
      输出主机
      ,以便在屏幕上的两次
      写入主机
      调用之间正确显示:

      Write-Host '------- 1'; Get-Item . | Select-Object FullName | Out-Host; Write-Host '------- 2'
      
    • 缺点:使用
      输出主机
      意味着您无法捕获或重定向命令的输出,因为它直接发送到主机(显示器)。此外,要(a)知道什么命令触发了问题,以及(b)记住对每个命令应用解决方法,都很麻烦

  • (b)
    Write Host
    调用替换为向成功输出流发送嵌入字符串(用于着色)。

    • 注意:在Windows 10或PowerShell[Core]v6上需要Windows PowerShell v5.1+

    • 缺点:字符串(彩色)将成为代码数据输出的一部分,因此在捕获/重定向输出时会包含这些字符串

      # 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字符),如
        。|其中对象{-not($\是[string]-和$\匹配'\e')}
嵌入VT转义序列允许您选择性地为字符串的部分着色

使用
写入主机
实现相同的效果需要使用
-NoNewline
进行多次调用

第三方cmdlet(模块)模拟
写入主机的语法,并使用
[控制台]
类型的属性打开和关闭着色,同时将字符串发送到成功输出流。
这对于以给定颜色编写整个字符串非常有效,但不能在一行上拼凑不同颜色的部分,因为单独写入成功输出流的每个字符串都会打印在自己的行上


如果您想在将VT序列直接嵌入字符串中时使用一个方便的包装器,您可以调整
Write HostColored
函数,将幕后发生的
Write Host
调用替换为VT序列。

您被指向了错误的帖子(这与您的问题无关-请参见此处). 对于彩色输出,您确实需要
写入主机
,否则您必须在发送到成功流/传递到
写入输出
的输出字符串中嵌入VT(虚拟终端)转义序列,但它们随后成为数据。要解决
写入主机
写入输出
/成功流输出之间的排序问题,请参阅,但请注意,它需要将数据输出发送到屏幕。非常感谢。如果你将此作为答案发布,我会将其标记为答案。我不喜欢微软制造的这种混乱。应该有一些明确的区别:写输出/写主机,我不清楚写主机是否会以如此奇怪的方式运行。这是一种脚本语言,我们应该期望事情是顺序的,第1行完全完成,然后第2行等等。我对一些做其他事情的特殊函数没有问题,但对大多数人来说,大多数时候,他们只是希望命令1在命令2之前执行。我想在过去,我遇到过这个问题“永远不要无缘无故地使用Write Host”,但我已经停止使用PowerShell很多年了,现在又重新使用它,所以我不得不再次经历这种痛苦(