如何在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

果朗

package main
import "fmt"
func main() {
fmt.Printf("Hello ąćęłńóśźż worldn")
}

结果

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

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

Quicksilver的答案基本正确:

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

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

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

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

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

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

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

        • 但是,在调用Python脚本之前,您可以使用$env:PYTHONUTF8=1来指示Python使用UTF-8(这将适用于从同一进程进行的所有Python调用);在v3.7+中,您也可以将命令行选项-X utf8(区分大小写)作为每次调用的opt-in传递
      • GoNode.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:SYSTEMCurrentControlSetControlNlsCodePage ACP))
python hello.py | Write-Output
# Restore the original encoding.
[Console]::OutputEncoding = $orig

您自己的答案提供了一个有效的替代方案,但它附带了注意事项

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

  • 显著的副作用包括:

    • Windows PowerShell的默认行为发生了更改,因为它使用ANSI代码页来读取源代码,并将其作为Get-ContentSet-Contentcmdlet的默认编码。

      • 例如,包含非ASCII范围字符(如é)的现有Windows PowerShell脚本将行为不端,除非它们被保存为UTF-8并带有BOM(或"Unicode"、UTF-16LE,它总是有BOM)。

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

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

  • 有关详细信息,请参阅此答案。


[1]跨平台PowerShell(Core)v6+版本始终使用(无BOM)UTF-8。虽然可以配置Unix终端,从而控制台(终端)应用程序使用UTF-8以外的字符编码,但现在很少这样做——UTF-8几乎被普遍使用

[2]相反,是$OutputEncoding首选变量决定了用于通过管道向外部程序发送文本的编码

解决方案是启用Beta: Use Unicode UTF-8 for worldwide language support,如What does"Beta:使用Unicode UTF-8支持全球语言";真的是这样吗?

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

Quciksilver此外,我发现Ghisler写的解释很有帮助(来源):

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

当启用此选项时,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_MACHINESYSTEMCurrentControlSetControlNlsCodePage]
"ACP"="65001"
"OEMCP"="65001"
"MACCP"="65001"

最新更新