在命令提示符/Windows PowerShell 中使用 UTF-8编码(CHCP 65001)(视窗10)

一段时间以来,我一直在强制使用命令提示和 Windows PowerShell 中的 chcp 65001,但是从 SO 和其他几个社区的问答帖子来看,它是 似乎是个危险而低效的解决方案。微软是否提供了一个改进的/完整的 chcp 65001替代品,可以永久保存,而无需手动更改注册表?如果没有,是否有一个公开宣布的时间表或议程,以支持 UTF-8在 Windows CLI 的未来?

就我个人而言,我一直在使用 chcp 949来支持韩文字符,但是在一些应用程序(比如 Neovim)中反斜杠 \的奇怪显示和不正确/不可理解的显示,以及通过 949不支持的 没有韩文字符最近似乎成了一个问题。

132407 次浏览

您可以将命令 chcp 65001放在 Powershell 配置文件中,当您打开 Powershell 时它将自动运行。但是,这对 cmd.exe 没有任何作用。

微软目前正在开发一种完全支持 Unicode 的改进型终端。它是 开源,如果您使用的是 Windows10Version1903或更高版本,您已经可以下载 预览版

此外,你亦可使用第三方虚拟终端,例如 终点站

注:

  • 这个答案显示了如何将 Windows 控制台中的 字符 < em > 编码 切换到
    UTF-8 (代码页 65001) ,使 贝壳cmd.exe和 PowerShell 在与 外部(控制台)程序通信时正确使用 完全支持 Unicode中的 编码和解码字符(文本) ,并且在 cmd.exe中也用于文件 I/O < sup > [1]

  • 相比之下,如果您关心的是控制台窗口中 Unicode字符局限性的独立方面,请参阅 这个答案的中间部分和底部部分,其中也讨论了替代控制台(终端)应用程序。


Microsoft 是否提供了一个改进的/完整的 chcp 65001的替代品,可以永久保存而无需手动更改注册表?

从(至少) 视窗10版本1903开始,您可以选择 将 < em > system locale (< em > 用于非 Unicode 程序的语言 )设置为 UTF-8,但是选择 在撰写本文时,功能是 < em > 仍处于 beta 中

激活它:

  • 运行 intl.cpl(在控制面板中打开区域设置)
  • 按照下面屏幕快照中的说明操作。

Control Panel > Region > Administrative

  • 这个 将系统的活动 OEM < em > 和 ANSI 代码页设置为 65001,即 UTF-8代码页,因此(a)使所有未来使用 OEM代码页的 控制台窗口默认使用 UTF-8(就好像 chcp 65001是在 cmd.exe窗口中执行的一样)和(b)也使遗留的非 Unicode GUI子系统应用程序,使用 ANSI代码页,使用 UTF-8。

    • 警告 :

      • 如果你正在使用 Windows PowerShell,这也会使得 Get-ContentSet-Content 以及其他 Windows PowerShell 默认的情况下,系统的活动 ANSI 代码页,特别是 从无 BOM 文件中读取 < em > 源代码 默认设置为 UTF-8(PowerShell 核心(v6 +)总是这样做)。这意味着,在没有 -Encoding参数的情况下,ANSI 编码的无 BOM 文件(这在历史上很常见)将被误读,用 Set-Content创建的文件将是 UTF-8而不是 ANSI 编码的。

      • [ 固定在 PowerShell 7.1中]至少到 PowerShell 7.0,基础.NET 版本(. NET Core 3.1)中的 < em > bug 会导致 PowerShell 中的后续 bug: 一个 UTF-8 BOM意外地被预先预置到通过 stdin 发送到外部进程的数据(不管你将 $OutputEncoding设置成什么) ,其中值得注意的是 突破 Start-Job-参见 GitHub 的问题

      • 不是所有的字体都使用 Unicode,所以选择 TT (TrueType)字体,但是即使是它们通常也只支持所有字符的 子集 ,所以你可能不得不尝试使用特定的字体来查看是否所有你关心的字符都被表示出来——详情请参阅 这个答案,它还讨论了其他控制台(终端)应用程序,这些应用程序有更好的 Unicode 渲染支持。

      • 正如 赤山指出的,不“说话”UTF-8的遗留控制台应用程序将仅限于 < em > ASCII-only input ,并且在尝试输出超出(7位) ASCII 范围的字符时将产生 < em > 错误输出 (在过时的 Windows7及以下版本中,程序甚至可能是 撞车)。
        如果运行遗留控制台应用程序对您很重要,请在评论中查看 eryksun 的建议。

  • 然而,对于 Windows PowerShell 来说,就是不够:

    • 另外还必须将 $OutputEncoding首选项变量设置为 UTF-8: $OutputEncoding = [System.Text.UTF8Encoding]::new()[2]; 将该命令添加到 $PROFILE(仅当前用户)或 $PROFILE.AllUsersCurrentHost(所有用户)文件中是最简单的。
    • 幸运的是,在 PowerShell 核心中不再需要这样做,它在内部始终默认为无 BOM 的 UTF-8。

如果在您的环境中将 系统区域设置系统区域设置设置为 UTF-8是 没有选项,那么改为使用 启动命令:

注意: 上面提到的关于遗留控制台应用程序的警告同样适用于此处。如果运行遗留控制台应用程序对您很重要,请在评论中查看 eryksun 的建议。

  • 对于 PowerShell (两个版本) ,在 $PROFILE(仅当前用户)或 $PROFILE.AllUsersCurrentHost(所有用户)文件中添加以下代码行,这相当于 chcp 65001,并辅以设置偏好变量 $OutputEncoding,以指示 PowerShell 通过 UTF-8管道向外部程序发送数据:

    • 注意,从 在里面运行一个 PowerShell 会话的 chcp 65001是有效的,因为。NET 在启动时缓存控制台的输出编码,并且不知道以后使用 chcp所做的更改; 另外,如前所述,Windows PowerShell要求设置 $OutputEncoding-请参阅 这个答案了解详细信息。
$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
  • 例如,这里有一种快速而简单的方法,可以通过编程方式将这一行添加到 $PROFILE:
'$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding' + [Environment]::Newline + (Get-Content -Raw $PROFILE -ErrorAction SilentlyContinue) | Set-Content -Encoding utf8 $PROFILE
  • 对于 cmd.exe ,通过注册表定义一个自动运行命令,值为键 HKEY_CURRENT_USER\Software\Microsoft\Command Processor(仅当前用户)或 HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor(所有用户)的 AutoRun:

    • 例如,您可以使用 PowerShell 为您创建这个值:
# Auto-execute `chcp 65001` whenever the current user opens a `cmd.exe` console
# window (including when running a batch file):
Set-ItemProperty 'HKCU:\Software\Microsoft\Command Processor' AutoRun 'chcp 65001 >NUL'

可选内容: 为什么 Windows PowerShell ISE是一个糟糕的选择:

尽管 ISE 确实比控制台支持更好的 Unicode 渲染,但它通常是一个糟糕的选择:

  • 首先,ISE 是过时的: 它不支持 PowerShell (核心)7 + ,所有未来的开发都将使用它,而且它也不是跨平台的,不像 PowerShell 两个版本的最新主要 IDE VisualStudio 代码,它已经默认使用了 PowerShell 核心的 UTF-8语言,并且可以配置为支持 Windows PowerShell。

  • ISE 通常是一个用于 发展中脚本的环境,而不是用于生产中的 运行它们(如果您正在为其他人编写脚本,那么您应该假设它们将在 控制台中运行) ; 值得注意的是,对于 < em > 运行 代码,ISE 的行为与普通控制台的行为不同:

    • 对运行 外部程序的支持不足,不仅是因为缺乏对 互动的支持(见下一点) ,还因为:

      • 字符编码 : ISE 错误地认为外部程序默认使用的是 ANSI代码页,而实际上它是 OEM代码页。例如,默认情况下,这个简单的命令尝试简单地将一个从 cmd.exe回显的字符串传递给故障(参见下面的修复程序) :
        cmd /c echo hü | Write-Output

      • 将 stderr 输出作为 PowerShell 错误呈现不当: 请参见 这个答案

    • ISE 点源脚本文件调用而不是在 儿童显微镜中运行它们(后者是在常规控制台窗口中发生的情况) ; 也就是说,重复调用在 非常相似的范围中运行。这可能导致微妙的错误,其中前一次运行留下的定义可能会影响后续的定义。

  • 正如 赤山指出的,ISE 不支持运行 < em > 交互式 外部控制台程序,即那些需要用户输入的:

问题在于它隐藏了控制台,并将进程输出(但不是输入)重定向到管道。当文件是管道时,大多数控制台应用程序会切换到完全缓冲。此外,交互式应用程序需要从 stdin 读取,这是不可能从隐藏的控制台窗口。(它可以通过 ShowWindow解除隐藏,但是单独的输入窗口很笨重。)

  • 如果你愿意接受这个限制,将活动代码页切换到 65001(UTF-8)以便与外部程序进行正确的通信需要一个笨拙的解决方案:

    • 您必须首先通过从内置控制台运行 任何外部程序来强制创建隐藏的控制台窗口,例如,chcp-您将看到一个控制台窗口的闪烁。

    • 只有 那么可以将 [console]::OutputEncoding(和 $OutputEncoding)设置为 UTF-8,如上所示(如果尚未创建隐藏控制台,则将获得 handle is invalid error)。


[1]在 PowerShell 中,如果从不调用 外部程序,就不必担心系统语言环境(活动代码页) : PowerShell 本机命令和。NET 调用总是通过 UTF-16字符串进行通信(本机。NET 字符串) ,并且对文件 I/O 应用独立于系统区域设置的默认编码。类似地,由于 Windows API 函数的 Unicode版本用于打印到控制台并从控制台读取,因此非 ASCII 字符总是正确打印(在控制台的呈现限制范围内)。
相比之下,在 cmd.exe中,系统语言环境对于文件 I/O 很重要(具有 <>重定向,但值得注意的是包括对于批处理文件源代码采用什么编码) ,而不仅仅是与内存中的外部程序通信(例如在 for /f循环中读取程序输出时)。

[2]在 PowerShell v4-中,当静态 ::new()方法不可用时,使用 $OutputEncoding = (New-Object System.Text.UTF8Encoding).psobject.BaseObject。请参阅 GitHub 第5763期了解为什么需要 .psobject.BaseObject部分。

Powershell ISE 显示的韩语非常好。下面是一个用 utf8编码的示例文本文件,它可以工作:

PS C:\Users\js> cat .\korean.txt


The Korean language (South Korean: 한국어/韓國語 Hangugeo; North
Korean: 조선말/朝鮮말 Chosŏnmal) is an East Asian language
spoken by about 77 million people.[3]

由于 ISE 随每个版本的 Windows10一起出现,我并不认为它过时。我不同意删除我原来答案的人。

ISE 有一些限制,但有些脚本可以通过外部命令完成:

echo 'list volume' | diskpart # as admin
cmd /c echo hi

编辑:

如果你有 Windows101903,你可以从微软商店 https://devblogs.microsoft.com/commandline/introducing-windows-terminal/下载 Windows 终端,韩文文本将在那里工作。Powershell 5需要的文本格式是 UTF8和 bom 或 UTF16。

编辑2:

似乎理想状态是 windows 终端 + powershell 7或 vscode + powershell 7,用于粘贴字符和输出。

编辑3:

即使在 EDIT2情况下,也不能粘贴某些 Unicode 字符,如 (U + 21 C6)或 Unicode 空间。只有奥斯克斯的 PS7才能用。