微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

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

但是我不能正确书写非ascii字符。

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

我尝试了 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):

如果选中此选项,Windows 将使用代码页 65001 (Unicode UTF-8) 而不是像 1252 (Western Latin1) 这样的本地代码页 所有纯文本文件。优点是在例如创建的文本文件 俄语语言环境也可以在其他语言环境中阅读,例如西方或 欧洲中部。缺点是只有 ANSI 的程序(大多数较旧的 程序)将显示垃圾而不是重音字符。

当启用此选项时,也是 7.1 has a bug 版之前的 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"
,

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

Quicksilver's answer 基本上是正确的:

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

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

    • 标准的 chcp 程序还报告活动的 OEM 代码页,虽然它原则上也可用于更改活动控制台的代码页(例如,{{ 1}}),由于 .NET 缓存编码,这在 inside PowerShell 中不能工作。

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

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

    • 值得注意的 CLI 尊重活动控制台代码页:

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

        • 但是,您可以在调用 Python 脚本之前使用 [Console]::OutputEncoding 来指示 Python 使用 UTF-8(然后适用于从同一进程进行的所有 Python 调用);在 v3.7+ 中,您也可以将命令行选项 $env:PYTHONUTF8=1(区分大小写)作为每次调用选择加入。
      • GoNode.js 总是使用 UTF-8 编码。

以下代码段显示了如何根据需要设置 -X utf8 临时

[Console]::OutputEncoding

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

  • 通过控制面板(或等效的注册表设置)激活 # 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 功能会更改代码页系统范围,这不仅会影响所有控制台窗口和控制台应用程序,以及传统(非 Unicode)GUI 子系统应用程序,前提是 OEM 和 ANSI 代码页都在设置中。

  • 显着的副作用包括:

    • Windows PowerShell 的默认行为发生了变化,因为它使用 ANSI 代码页来读取源代码并作为Your own answerGet-Content cmdlet 的默认编码。

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

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

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

  • 有关详细信息,请参阅 Set-Content


[1] 跨平台 this answer 始终使用(无 BOM)UTF-8。虽然可以配置 Unix 终端,从而配置控制台(终端)应用程序以使用 other UTF-8 以外的字符编码,但如今这样做很少见 - UTF-8 几乎普遍使用。上>

[2] 相比之下,PowerShell (Core) v6+ edition 决定了用于通过管道发送文本外部程序的编码。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。