在 Powershell 显示 Unicode

尽管 Powershell 试图让这一切变得困难,但我试图实现的目标应该是相当直截了当的。

我想显示文件的完整路径,其中一些文件的名称中包含阿拉伯文、中文、日文和俄文字符

我总是得到一些无法破译的输出,如下面所示 enter image description here

控制台中看到的输出正在被另一个脚本消耗。 输出包含 而不是实际字符。

执行的命令是

(Get-ChildItem -Recurse -Path "D:\test" -Include *unicode* | Get-ChildItem -Recurse).FullName

有没有什么简单的方法可以启动 powershell (通过命令行或以任何可以写入脚本的方式) ,以便正确地看到输出。

另外,关于 Stack Overflow,我已经回答了很多类似的问题,但是除了称之为 Windows 控制台子系统问题之外,没有一个问题有更多的输入。

92383 次浏览

确保您有一个包含所有问题字符的字体安装和设置为您的 Win32控制台字体。如果我没记错的话,单击窗口左上角的 PowerShell 图标并选择 Properties。结果弹出对话框应该有一个设置所用字体的选项。它可能必须是位图(.FON.FNT)字体。

注:

  • 在 Windows 上,对于 < em > 渲染 Unicode 字符来说,最重要的是 选择 < em > font /< em > 控制台(终端) 应用程序

    • 如今,使用从 Windows 10开始通过微软商店发布和更新的 Windows 终端是对 遗留控制台主机(由 conhost.exe提供的控制台窗口)的一个很好的替代,提供了卓越的 Unicode字符支持。在 Windows1122H2中,Windows 终端甚至是 成为 < em > default 控制台(终端)
  • 关于 < em > 程序化处理 Unicode字符 当与外部程序通信时$OutputEncoding[Console]::InputEncoding[Console]::OutputEncoding物质也-见下文。


PowerShell < em > Core (v6 +)透视图(参见下一节的 Windows PowerShell) ,与字符 渲染问题无关(也将在下一节中讨论) ,关于与外部程序的通信:

  • 类 Unix 平台上,PowerShell Core 使用 默认情况下,UTF-8 < em > (如今,由于现代类 Unix 平台使用基于 UTF-8的语言环境,因此通常使用 默认情况下,UTF-8 < em > )。

  • 窗户 上,它是所有控制台中的 遗留 < em > 系统区域设置 ,通过其 < em > OEM 代码页 确定默认编码,包括 Windows PowerShell 和 PowerShell Core 控制台窗口,尽管是 Windows10的最新版本现在允许将系统语言环境设置为代码页 65001(UTF-8),注意到这个功能在本文写作时仍处于 beta 测试阶段,使用它有 影响深远-参见 这个答案

    • 如果您确实使用了这个特性,PowerShell 核心控制台窗口将自动支持 UTF-8,尽管在 Windows PowerShell中您仍然需要将 $OutputEncoding设置为 UTF-8(在 Core 中已经默认为 UTF-8) ,如下所示。

    • 否则——尤其是在较老的 Windows 版本上——你可以使用下面详细介绍的相同方法进行 Windows PowerShell。


使您的 Windows PowerShell控制台窗口 Unicode (UTF-8)感知 :

  • 选择一个支持特定 剧本(书写系统,字母表)的 TrueType(TT)字体 ,您希望在控制台中正确显示这些字符:

    • 重要 : 虽然 原则上,所有 TrueType 字体都支持 Unicode < em > ,他们 通常只支持所有 Unicode 字符的 < em > 子集 ,即那些对应于特定的 < em > script (编写系统)的字符,如拉丁文的脚本,西里尔(俄罗斯)脚本,..。
      在您的特殊情况下-如果您必须支持阿拉伯文以及中文、日文和俄文字符-您唯一的选择是 SimSun-ExtB,这是可在 只有 Windows10
      有关 Windows 字体针对哪些脚本(字母)的列表,请参见 维基百科

    • 要更改字体,请单击窗口左上角的图标并选择 Properties,然后切换到 Fonts选项卡并选择感兴趣的 TrueType 字体。

  • 此外,为了与 外部程序进行适当的沟通,请参见:

    • 控制台窗口的代码页必须切换到 65001,UTF-8代码页(通常使用 chcp 65001完成,但是不能在 PowerShell session[1]中直接使用,但是下面的 PowerShell 命令具有相同的效果)。

    • 当发送流水线输入的 外部程序时,Windows PowerShell 必须被指示使用 UTF-8来与 外部设施通信(在解码输出的 来自外部程序时,应用的是存储在 [console]::OutputEncoding中的编码)。

以下 Windows PowerShell 中的魔法咒语可以做到这一点(如上所述,这个 abc1执行 chcp 65001) :

$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding =
New-Object System.Text.UTF8Encoding

对于 坚持,这些设置 ,也就是说,为了使未来的交互式 PowerShell 会话默认支持 UTF-8,将上面的命令添加到 $PROFILE文件中。

注意: 最新版本的 Windows 10现在允许使用 将 < em > system locale 设置为代码页 65001(UTF-8)(该功能在1903版本的 Windows 10中仍处于 beta 测试阶段) ,这使得 abc2控制台 Windows 默认使用 UTF-8,包括 Windows PowerShell。
如果您确实使用了这个特性,那么设置 [console]::InputEncoding/[console]::OutputEncoding将不再是严格必要的,但是您仍然必须设置 $OutputEncoding(这在 PowerShell 核心中是不必要的,其中 $OutputEncoding已经默认为 UTF-8)。

重要提示 :

  • 这些 设置假定与之通信的任何外部实用程序都需要使用 UTF-8编码的输入并生成 UTF-8输出

    • 例如,用 Node.js 编写的 CLI 满足了这个条件。
    • Python 脚本——如果在编写时考虑到对 UTF-8的支持——也可以处理 UTF-8。
  • 相比之下,这些设置可以按照系统的遗留 OEM 代码页所暗示的 只需要单字节编码的 < em > break (旧的)实用程序

    • 到 Windows 8.1,这甚至包括标准的 Windows 实用程序,如 find.exefindstr.exe,这已经在 Windows 10中修复。
    • 请参阅本文的底部,了解如何通过切换到 UTF-8只是暂时的来调用给定的实用程序来绕过这个问题。
  • 这些设置适用于 仅限于外部程序,与 PowerShell 的 cmdlet在输出 上使用的编码无关:

    • 请参阅 这个答案了解 PowerShell cmdlet 使用的默认字符编码; 简而言之: 如果您希望 Windows PowerShell中的 cmdlet 默认为 UTF-8(PowerShell [ Core ] v6 + 无论如何都会这样做) ,那么将 $PSDefaultParameterValues['*:Encoding'] = 'utf8'添加到您的 $PROFILE中,但是请注意,这将影响在您的会话中使用 -Encoding参数对 cmdlet 的所有调用,除非明确使用该参数; 还请注意,在 Windows PowerShell中您将总是得到 UTF-8文件 $PROFILE0; 相反,在 PowerShell [ Core ] v6 + 中,默认为 BOM-$PROFILE2 UTF-8(在没有 -Encoding和使用 -Encoding utf8的情况下,您都必须使用 'utf8BOM'

可选的背景资料

对于他所有的输入,向 赤山致敬。

  • 当 TrueType 字体处于活动 状态时,控制台窗口 缓冲器将正确地保留(非 ASCII) Unicode 字符。即使它们不能正确地使用 呈现 ; 也就是说,即使它们可以通用地使用 出现作为 ?,以表明当前字体缺乏支持,您也可以在其他地方使用 复制粘贴这样的字符而不会丢失信息,就像 eryksun 指出的那样。

  • PowerShell 能够输出 Unicode 字符 到控制台,甚至不需要先切换到代码页 65001
    然而,没有本身确保了其他程序可以正确地处理这样的输出——见下文。

  • 谈到 通过电子邮件与外部程序进行沟通,PowersShell 使用的是 $OutputEncoding偏好变量中指定的字符编码,即 在 Windows PowerShell 中默认为 ASCII (!),这意味着任何非 ASCII 字符都会被音译成 字面意思 ?字符,从而得到 信息丢失。(相比之下,值得称道的是,PowerShell 核心(v6 +)现在始终使用(BOM-less) UTF-8作为默认编码。)

    • 然而,相比之下,传递非 ASCII < em > 参数 (而不是 stdout (管道输出)输出) 到外部程序似乎需要 < em > no 特殊配置(我不清楚它为什么有效) ; 例如,下面的 Node.js 命令即使使用默认配置也能正确返回 €: 1:
      node -pe "process.argv[1] + ': ' + process.argv[1].length" €
  • [Console]::OutputEncoding :

    • 控制当控制台将程序输出转换为控制台显示字符时所假定的字符编码。
    • 也是 告诉 < em > PowerShell 当 < em > 从外部程序捕获输出 时采用什么编码
      结果是,如果您需要从一个生成 UTF-8的程序中获取 捕获输出捕获输出,那么您还需要将 [Console]::OutputEncoding设置为 UTF-8; 设置 $OutputEncoding仅涵盖 输入(外部程序)方面。
  • [Console]::InputEncoding 键盘输入的编码设置为控制台 [2],并确定 PowerShell 的 CLI如何解释它通过 Stdin(标准输入)接收的数据。

  • 如果在 整个疗程中不能将控制台切换到 UTF-8,那么可以这样做 暂时的,为了一个给定的电话 :

      # Save the current settings and temporarily switch to UTF-8.
    $oldOutputEncoding = $OutputEncoding; $oldConsoleEncoding = [Console]::OutputEncoding
    $OutputEncoding = [Console]::OutputEncoding = New-Object System.Text.Utf8Encoding
    
    
    # Call the UTF-8 program, using Node.js as an example.
    # This should echo '€' (`U+20AC`) as-is and report the length as *1*.
    $captured = '€' | node -pe "require('fs').readFileSync(0).toString().trim()"
    $captured; $captured.Length
    
    
    # Restore the previous settings.
    $OutputEncoding = $oldOutputEncoding; [Console]::OutputEncoding = $oldConsoleEncoding
    
  • 老版本 Windows (W10之前)的问题 :

    • 65001的活动 chcp值破坏了一些外部程序的控制台输出,甚至老版本 Windows 中的批处理文件,这可能最终源于 WriteFile() Windows API 函数中的一个 bug (标准 C 库也使用了这个函数) ,该函数错误地报告了 角色的数量,而不是 字节,代码页 65001正在生效,正如在 这篇博文中所讨论的那样。
  • 根据 弹跳在2008年对 这个答案的评论,结果的症状是: “我的理解是,返回一个字节数(比如 fread/fwrite/etc)的调用实际上返回一个字符数。这会导致各种各样的症状,比如不完整的输入读取、挂起刷新、批处理文件损坏等等。”


本机 Windows 控制台(终端)的高级替代方案,conhost.exe

Eryksun 建议使用两个提供商 更好更快的 Unicode字符本机 Windows 控制台窗口的替代方案(conhost.exe) ,因为它们使用了现代的、 GPU 加速的 DirectWrite/DirectX API,而不是“不能处理复杂脚本、非 BMP 字符或自动回退字体的旧 GDI 实现”

  • 微软自己的开源 Windows 终端,从 Windows10开始就通过微软商店进行发布和更新,请参阅 给你了解相关介绍。

  • 历史悠久的第三方选择 一个 href = “ https://ConEmu.github.io/”rel = “ nofollow norefrer”> ConEmu ,它的优势在于可以在较老的 Windows 版本上工作。


[1]注意,从 在里面运行一个 PowerShell 会话是 没有有效的,因为。NET 储藏室控制台在启动时的输出编码,并且不知道后来用 chcp做的更改(只有通过 [console]::OutputEncoding]直接做的更改被拾取)。

[2]我不清楚这在实践中是如何体现的; 如果你知道,请告诉我们。

为了测试目的,我从不同的 Unicode 子区域创建了一些带有 有效的名字的文件夹和文件,如下所示:

valid names

例如,在 PowerShell 控制台中,使用 新快递控制台字体显示替换符号而不是 CJK 字符:

Courier New

另一方面,使用 西姆 · 孙控制台字体时,显示的是替换符号,而不是阿拉伯文和希伯来文字符,而 CJK 字符的显示似乎是正确的:

SimSun

请注意,所有替换符号仅仅是 展示,而实际字符是保留的,正如您在上面 PowerShell 控制台的以下 复制和粘贴中看到的:

PS D:\PShell> (Get-ChildItem 'D:\bat\UnASCII Names\' -Dir).Name
Arabic (عَرَبِيّ‎)
CJK (中文(繁體))
Czech (Čeština)
Greek (Γρεεκ)
Hebrew (עִבְרִית)
Japanese (日本語)
MathBoldScript (𝓜𝓪𝓽𝓱𝓑𝓸𝓵𝓭𝓢𝓬𝓻𝓲𝓹𝓽)
Russian (русский язык)
Türkçe (Türkiye)
‹angles›
☺☻♥♦

为了完整起见,以下是适合于 为命令提示符启用更多字体的注册表值(对于 Windows PowerShell 控制台也适用) :

(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont' |
Select-Object -Property [0-9]* | Out-String).Split(
[System.Environment]::NewLine,
[System.StringSplitOptions]::RemoveEmptyEntries) |
Sort-Object

输出样本:

0       : Consolas
00      : Source Code Pro
000     : DejaVu Sans Mono
0000    : Courier New
00000   : Simplified Arabic Fixed
000000  : Unifont
0000000 : Lucida Console
932     : *MS ゴシック
936     : *新宋体

Powershell ISE 是一个用于显示外部字符的选项: korean.txt是一个 UTF8编码的文件:

PS C:\Users\js> get-content korean.txt


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

与亚马逊翻译服务公司合作时,我也面临着类似的挑战。我从 Windows 商店安装了终端,它现在为我工作!

如果您安装 微软的“ Windows 终端”从微软商店(或预览版) ,它将预先配置为完全 Unicode 本地化。

Windows Terminal Preview with snowman ⛄, Arabic (عَرَبِيّ‎), CJK (中文(繁體)), Czech (Čeština), Greek (Γρεεκ), Hebrew (עִבְרִית), Japanese (日本語), MathBoldScript (𝓜𝓪𝓽𝓱𝓑𝓸𝓵𝓭𝓢𝓬𝓻𝓲𝓹𝓽), Russian (русский язык), Türkçe (Türkiye), ‹angles›, ☺☻♥♦

您仍然不能使用特殊字符 输入命令... ... 除非您使用 WSL!

Using wsl, we are able to run echo "snowman ⛄"

刚刚注册,只是为了解释为什么“露西达控制台”作为字体在 Powershell ISE 有效。不幸的是,我不能评论由于缺少声誉,所以这里作为答案:

在普通的 Powershell 中,所有字符都以配置好的字体显示。这就是为什么例如中文或西里尔字符与“ Lucida Console”和许多其他字体断开的原因。 对于中文字母,Powershell ISE 会自动将字体更改为“灯仙”。

你可以通过将字体复制到 Word 或者类似的能够显示不同字体的程序中来找到你的特殊字符使用的替代字体。