CMD管道与PowerShell管道有何不同?

6

我正在尝试将Node.js的输出导入到preatty-pino中。

node .\dist\GameNode.js | pino-pretty

在CMD中运行这个命令可以得到格式化后的输出,但是在PowerShell中运行却没有任何输出。据我所知,PowerShell在进行管道传输时使用对象,因此我尝试了以下命令:

node .\dist\GameNode.js | Out-String -Stream | pino-pretty

但这也无法正常工作。

为什么这个命令在CMD中可用,在PowerShell中却不行呢?谢谢 :)

1个回答

8
注意:下面提供的信息没有解决问题中描述的具体 "pino-pretty" 问题。Lukas(提问者)已经在这里提交了错误报告here
令人惊讶的是,你什么都没得到,但是根本的区别是:
  • cmd.exe的管道传输原始数据,即字节流(接收数据的程序可能会或可能不会将其解释为文本)。

  • PowerShell的管道,在与外部程序通信时,只传输文本(字符串),至少在PowerShell 7.3.x版本中如此,这有两个含义:

    • 在将数据通过管道传输到外部程序时,必须对文本进行编码,这是根据存储在首选项变量$OutputEncoding中的字符编码进行的。

    • 在从外部程序接收数据时,必须对数据进行解码,这是根据存储在[Console]::OutputEncoding中的字符编码进行的,默认情况下是系统的OEM代码页,如chcp所示。

      • 无论数据是否在PowerShell中进一步处理或传递给另一个外部程序,这种解码都会始终发生

        • 这种有时会引起问题的无法在PowerShell的管道中直接发送原始数据,即使是在两个外部程序之间的能力,在此答案中进行了讨论。
      • 唯一的例外是如果外部程序的输出既没有被捕获,也没有通过管道传输,也没有重定向到文件:在这种情况下,数据将直接打印到控制台(终端),但仅限于本地控制台(使用PowerShell远程操作与远程机器交互时,解码又会再次发生)。

        • 这种直接打印到显示屏的方式有时会隐藏编码问题,因为某些程序,特别是python,在这种情况下使用全面的Unicode支持;也就是说,输出可能会正常打印,但当您尝试进一步处理时,编码问题可能会浮出水面。
        • 一个强制解码的简单方法将调用括在(...);例如:
          python -c "print('eé')"可以正常打印,但
          (python -c "print('eé'))"会出现编码问题;请参阅底部部分获取更多信息。

尽管控制台应用程序传统上使用活动的 OEM 代码页来进行字符编码和解码,但 Node.js 总是使用 UTF-8。

因此,为了使 PowerShell 能够与 Node.js 程序正常通信,您必须(暂时)首先设置以下内容:

$OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()

如果您想要根本地切换到UTF-8,无论是系统范围内(这将产生深远的影响)还是仅限于PowerShell控制台窗口,请参阅此答案


顺便说一句:中间的Out-String -Stream管道段对于传递外部程序的输出是不必要的,因为逐行流式传输stdout输出是PowerShell的默认行为。换句话说,在你的情况下,这没有什么意义,实际上只是一个(代价高昂的)空操作。

可选阅读:方便的函数Invoke-WithEncoding和诊断函数Debug-NativeInOutput用于临时编码需求/诊断:

如果将所有PowerShell控制台切换为UTF-8不是一个选择,或者您需要处理使用特定编码而不是UTF-8或活动OEM代码页的“流氓”程序,您可以安装:

  • 函数Invoke-WithEncoding,它在调用外部程序时临时切换到给定的编码,直接从this Gist安装如下(我可以向您保证这样做是安全的,但您应该始终进行检查):
# Download and define advanced function Invoke-WithEncoding in the current session.
irm https://gist.github.com/mklement0/ef57aea441ea8bd43387a7d7edfc6c19/raw/Invoke-WithEncoding.ps1 | iex
  • 功能Debug-NativeInOutput,可以直接从this Gist(再次强调,你应该先检查)中获取,用于帮助诊断与外部程序相关的编码问题。
# Download and define advanced function Debug-NativeInOutput in the current session.
irm https://gist.github.com/mklement0/eac1f18fbe0fc2798b214229b747e5dd/raw/Debug-NativeInOutput.ps1 | iex

下面是使用python命令打印重音字符的示例命令。
与Node.js类似,Python的行为是非标准的,虽然它不使用UTF-8,而是使用系统的活动ANSI(!)代码页(而不是预期的OEM代码页)。
也就是说,即使您将PowerShell控制台切换为UTF-8,在默认情况下与Python脚本的通信也不会正常工作,除非进行额外的努力,而Invoke-WithEncoding可以为您封装这些操作:
注意:我在这里使用Python作为示例来说明这些函数的工作原理。实际上可以让Python使用UTF-8,具体方法是通过将环境变量PYTHONUTF8设置为1,或者在v3.7+版本中通过传递参数-X utf8(区分大小写)。

Invoke-WithEncoding 示例:

# Outputs *already-decoded* output, so if the output *prints* fine, 
# then *decoding* worked fine too.
PS> Invoke-WithEncoding { python -c "print('eé')" } -Encoding Ansi -WindowsOnly
eé

请注意,`Invoke-WithEncoding` 确保在输出之前对 .NET 字符串进行了实际解码,以防止编码问题在 Windows 上的直接显示输出中被意外掩盖(有关更多信息,请参见下文)。
`-WindowsOnly` 用于跨平台兼容性,并确保在此情况下仅在 Windows 上应用编码(在 Unix 上,Python 使用 UTF-8)。

Debug-NativeInOutput示例:

使用默认的PowerShell控制台,使用系统的OEM代码页,在相同的Python命令调用下,您将看到以下输出,从PowerShell (Core) 7.1中调用:

PS> Debug-NativeInOutput { python -c "print('eé')" }

Debug-NativeInOutput

注意DecodedOutput属性,它显示了基于将Python的输出解释为OEM而不是ANSI编码的结果:'eΘ'。(Input*属性为空,因为该命令没有涉及将数据通过管道传递给Python脚本。)
相比之下,使用直接打印到显示器时,输出会正常打印(因为Python只有在这种情况下才使用Unicode),这样就隐藏了问题,但是一旦你想要以编程方式处理输出 - 将其捕获到变量中,发送到管道中的另一个命令,或者重定向到文件中 - 编码问题就会浮出水面。
Invoke-WithEncoding类似,Debug-NativeInOutput也支持-Encoding参数,因此如果你在上述调用中传递-Encoding Ansi,你会看到Python的输出被正确解码。
输出反映了在PowerShell(Core)中,默认的$OutputEncoding为UTF-8,而在Windows PowerShell中,默认为ASCII(!)。这与控制台窗口中实际生效的编码不匹配,这是一个问题,GitHub问题#14945上的评论提出了一种未来解决此问题的方法(仅适用于PowerShell(Core))。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接