如何在 Powershell 中将程序的非 ascii 输出保存到文件?

How to save to file non-ascii output of program in Powershell?

我想 运行 在 Powershell 中编程并使用 UTF-8 编码将输出写入文件。

但是我无法正确写入非 ascii 字符。

我已经阅读了很多关于 Stack overflow 的类似问题,但我仍然找不到答案。

我尝试了 PowerShell 5.1.19041.1023PowerShell Core 7.1.3,它们对输出文件的编码不同,但内容以相同的方式损坏。


我尝试了 Python 和 Golang 中的简单程序:

(请假设我无法更改程序的源代码)

Python

print('Hello ąćęłńóśźż world')

结果:

python hello.py

Hello ąćęłńóśźż world

python hello.py > file1.txt

Hello ╣Šŕ│˝ˇťč┐ world

python hello.py | out-file -encoding utf8 file2.ext

Hello ╣Šŕ│˝ˇťč┐ world

cmd:

python hello.py > file3.txt

Hello ���� world

Golang

package main

import "fmt"

func main() {
    fmt.Printf("Hello ąćęłńóśźż world\n")
}

结果:

go run hello.go:

Hello ąćęłńóśźż world

go run hello.go > file4.txt

Hello ─ů─ç─Ö┼é┼ä├│┼Ť┼║┼╝ world

go run hello.go | out-file -encoding utf8 file5.txt

Hello ─ů─ç─Ö┼é┼ä├│┼Ť┼║┼╝ world

cmd 上工作正常:

go run hello.go > file6.txt

Hello ąćęłńóśźż world

您应该先设置控制台的OutputEncoding 属性。

在 PowerShell 中,在 运行 您的程序之前输入此行:

[Console]::OutputEncoding = [Text.Encoding]::Utf8

然后您可以将 Out-File 与您的编码类型一起使用:

py hello.py | Out-File -Encoding UTF8 file2.ext
go run hello.go | Out-File -Encoding UTF8 file5.txt

解决方案是启用 Beta: Use Unicode UTF-8 for worldwide language support,如 What does "Beta: Use Unicode UTF-8 for worldwide language support" actually do?

中所述

注意:此解决方案可能会导致遗留程序出现问题。请阅读 mklement0 的回答和 Quciksilver 的回答以获取详细信息和替代解决方案。

我还发现 Ghisler 写的解释很有帮助 (source):

If you check this option, Windows will use codepage 65001 (Unicode UTF-8) instead of the local codepage like 1252 (Western Latin1) for all plain text files. The advantage is that text files created in e.g. Russian locale can also be read in other locale like Western or Central Europe. The downside is that ANSI-Only programs (most older programs) will show garbage instead of accented characters.

启用此选项时,7.1 版之前的 Powershell 。如果启用它,您可能需要升级到版本 7.1 或更高版本。

我喜欢这个解决方案,因为它只需设置一次就可以了。它为 Windows 带来了一致的类 Unix UTF-8 行为。我希望我不会看到任何问题。


如何启用:

  1. Win+R → intl.cpl
  2. Administrative 选项卡
  3. 单击 Change system locale 按钮
  4. 启用Beta: Use Unicode UTF-8 for worldwide language support
  5. 重启

或者通过 reg 文件:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]
"ACP"="65001"
"OEMCP"="65001"
"MACCP"="65001"

注意:这些 字符编码问题只困扰 PowerShell 在 Windows 的两个版本中。在类 Unix 平台上,一直使用 UTF-8[1]

基本正确:

  • 存储在[Console]::OutputEncoding中的字符编码决定了PowerShell如何解码从外部程序接收到的文本[2] - 请注意它总是将此类输出解释为文本(字符串)。

    • [Console]::OutputEncoding 默认反映控制台的活动代码页,它本身默认为系统的活动 OEM 代码页,例如 437 (CP437) 在美式英语系统上。

    • 标准 chcp 程序也报告活动的 OEM 代码页,虽然原则上它也可以用于 更改 活动控制台(例如 chcp 65001),这 notinside PowerShell 工作,由于 .NET 缓存编码。

  • 因此,您可能必须(暂时)设置 [Console]::OutputEncoding 以匹配给定外部控制台程序使用的实际字符编码:

    • 虽然许多控制台程序遵循 活动控制台代码页(在这种情况下不需要解决方法),但有些程序不,通常是为了提供完整的 Unicode 支持。请注意,您可能不会注意到问题,直到您以编程方式处理这样一个程序的输出(意思是:捕获一个变量,通过管道发送到另一个命令,重定向到文件),因为这样的程序可能会检测到它的标准输出直接连接到控制台的情况,然后可能会选择性地使用完整的 Unicode 支持 来显示 .

    • 遵守活动控制台代码页的著名 CLI:

      • Python 表现出非标准行为,因为它默认使用活动的 ANSI 代码页,即代码页通常仅由非 Unicode GUI-子系统应用程序使用。

        • 但是,您可以使用 $env:PYTHONUTF8=1 before 调用 Python 脚本来指示 Python 使用 UTF-8(然后应用来自同一进程的所有 Python 调用);在 v3.7+ 中,您可以选择传递命令行选项 -X utf8(区分大小写)作为每次调用选择加入。
      • 还有Node.js总是用UTF-8编码。

以下代码段显示了如何根据需要临时[Console]::OutputEncoding

# Save the original encoding.
$orig = [Console]::OutputEncoding

# Work with console programs that use UTF-8 encoding,
# such as Go and Node.js
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()

# Piping to Write-Output is a dummy operation that forces
# decoding of the external program's output, so that encoding problems would show.
go run hello.go | Write-Output

# Work with console programs that use ANSI encoding, such as Python.
# As noted, the alternative is to configure Python to use UTF-8.
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP))

python hello.py | Write-Output

# Restore the original encoding.
[Console]::OutputEncoding = $orig

提供了一个 有效的替代方案,但它带有 警告 :

  • 通过控制面板(或等效的注册表设置)激活 Use Unicode UTF-8 for worldwide language support 功能更改代码页 系统范围 ,这不仅会影响所有控制台 windows 和控制台应用程序,还会影响遗留(非 Unicode)GUI-子系统应用程序,因为 OEM 和正在设置 ANSI 代码页。

  • 显着的副作用包括:

    • Windows PowerShell 的默认行为发生变化,因为它同时使用 ANSI 代码页阅读 源代码 并作为 Get-Content and Set-Content cmdlet 的默认编码。

      • 例如,包含非 ASCII 范围字符(例如 é)的现有 Windows PowerShell 脚本将出现错误行为,除非它们被保存为带有 BOM 的 UTF-8 (或保存为“Unicode”,UTF-16LE,它总是有一个 BOM)。

      • 相比之下,PowerShell (Core) v6+ 始终使用(无 BOM)UTF-8 开头。

    • 旧的控制台应用程序可能 破坏 65001 (UTF-8) 作为活动的 OEM 代码页,因为它们可能无法处理 UTF-8 的可变长度编码方面(单个字符最多可以编码 4 个字节)。

  • 有关详细信息,请参阅


[1] 跨平台 PowerShell (Core) v6+ edition 一贯使用(无 BOM)UTF-8。虽然可以配置 Unix 终端,从而使控制台(终端)应用程序使用字符编码 other 而不是 UTF-8,但如今很少这样做 - UTF-8 几乎被普遍使用.

[2] 相比之下,$OutputEncoding preference variable 决定了用于发送文本 外部程序的编码,通过 管道.