在Powershell中显示Unicode

bttbmeg0  于 2022-12-23  发布在  Shell
关注(0)|答案(7)|浏览(185)

我试图实现的应该是相当简单的,虽然Powershell试图使它很难。
我希望显示文件的完整路径,其中一些文件的名称中包含阿拉伯文、中文、日文和俄文字符
我总是得到一些无法辨认的输出,如下面所示的

在控制台中看到的输出正由另一个脚本使用。输出包含 而不是实际字符。
执行的命令为

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

有没有简单的方法来启动powershell(通过命令行或任何可以写入脚本的方式),以便正确地看到输出。
我已经经历了许多类似的问题堆栈溢出,但他们都没有太多的输入以外,称之为Windows控制台子系统的问题。

ssgvzors

ssgvzors1#

注:

  • 在Windows上,关于呈现 * Unicode字符,主要是**选择 * 字体/控制台(终端) 应用程序**。
  • 如今,使用**Windows Terminal**(自Windows 10起通过Microsoft Store分发和更新)是 * 传统控制台主机 *(conhost.exe提供的控制台窗口)的良好替代品,提供了卓越的Unicode字符支持。在Windows 11 22H2中,Windows终端甚至支持became the default console (terminal)
  • 关于*在与 * 外部程序*通信时以编程方式处理 * Unicode字符$OutputEncoding[Console]::InputEncoding[Console]::OutputEncoding也很重要-参见下文。
    • PowerShell * 核心***(v6+)透视图(有关 * Windows PowerShell ,请参见下一节),无论字符 * 呈现 * 问题如何(也将在下一节中介绍),都与 * 与外部程序通信有关:
  • 类Unix * 平台上,PowerShell Core默认使用**UTF-8***(通常,现在的类Unix平台使用基于UTF-8的语言环境)。
      • 在 * Windows*上,旧 * 系统区域设置 * 通过其 * OEM代码页 * 确定所有控制台(包括Windows PowerShell和PowerShell Core控制台窗口)中的默认编码,尽管最新版本的Windows 10现在允许将系统区域设置设置为代码页65001(UTF-8);请注意,截至本文撰写之时,该特性仍处于测试阶段,使用它会产生深远的影响**-请参见this answer
  • 如果您使用该功能,PowerShell * Core * 控制台窗口将自动识别UTF-8,但在 * Windows PowerShell * 中,您仍需将$OutputEncoding也设置为UTF-8(在Core中已默认为UTF-8),如下所示。
  • 否则-特别是在较旧的Windows版本上-您可以使用与下面详细介绍的Windows PowerShell相同的方法。
    • 使您的 * Windows PowerShell * 控制台窗口支持Unicode(UTF-8)**:
      • 选择一种 * TrueType (TT)字体*,它支持您希望在控制台中正确显示其字符的特定 * 脚本 *(书写系统、字母):
      • 重要信息**:虽然所有TrueType字体原则上*都支持Unicode *,但它们**通常只支持所有Unicode字符的 * 子集 *,即对应于特定 * 脚本 *(书写系统)**的字符,如拉丁脚本、西里尔(俄语)脚本...

在您的特定情况下-如果您必须支持阿拉伯语以及中文、日语和俄语字符-您唯一的选择是**SimSun-ExtB,该选项仅在Windows 10上可用**。
请参阅Wikipedia以获取Windows字体针对哪些脚本(字母)的列表。

  • 要更改字体,请单击窗口左上角的图标并选择Properties,然后切换到Fonts选项卡并选择感兴趣的TrueType字体。
  • 请参阅this SU answer by not2quibit以了解如何使其他字体可用。
      • 此外,为了与 * 外部程序***正确通信:
      • 控制台窗口的代码页必须切换到65001**,UTF-8代码页(通常使用chcp 65001完成,但是不能直接在PowerShell会话[1]中使用,但下面的PowerShell命令具有相同的效果)。
      • 必须指示Windows PowerShell也**使用UTF-8与 * 外部实用程序 * 通信,无论是在通过$OutputEncoding首选项变量将管道输入 * 发送到 * 外部程序时(在解码 * 来自 * 外部程序的输出时,应用存储在[console]::OutputEncoding中的编码)。

Windows PowerShell中的以下神奇咒语可以做到这一点(如前所述,这 * 隐式 * 执行chcp 65001):

$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding =
                    New-Object System.Text.UTF8Encoding
    • 要 * 保留 * 这些设置**,即,使您将来的交互式PowerShell会话默认支持UTF-8,请将上述命令添加到您的$PROFILE文件中。

注:Windows 10的最新版本现在允许setting the system locale to code page 65001 (UTF-8)(该功能在Windows 10版本1903时仍处于测试阶段),这使得 * 所有 * 控制台窗口默认为UTF-8,包括Windows PowerShell的。
如果您确实使用了该功能,则不再需要设置[console]::InputEncoding/[console]::OutputEncoding,但您仍然需要设置$OutputEncoding(在PowerShell * Core * 中不需要,因为$OutputEncoding已经默认为UTF-8)。

    • 重要信息**:
  • 这些设置假定您与之通信的任何外部实用程序都期望UTF-8编码的输入并生成UTF-8输出
  • 例如,用Node.js编写的CLI满足该标准。
  • Python脚本--如果在编写时考虑到UTF-8支持--也可以处理UTF-8。
  • 相比之下,这些设置可能会**破坏 (较旧的)实用程序,这些实用程序只需要单字节编码,正如系统的旧OEM代码页所暗示的那样。
  • 直到Windows 8.1,这甚至包括标准的Windows实用程序,如find.exefindstr.exe,这些程序已在Windows 10中修复。
  • 请参阅本文末尾,了解如何通过在调用给定实用程序时临时切换到UTF-8 * 来绕过这个问题。

*这些设置 * 仅适用于外部程序 *,与 *PowerShell的cmdlet * 在输出中使用的编码无关:

  • 有关PowerShell cmdlet使用的默认字符编码,请参阅this answer;简而言之:如果您希望 Windows PowerShell 中的cmdlet默认为UTF-8(PowerShell [Core] v6+ 无论如何都会这样做),请将$PSDefaultParameterValues['*:Encoding'] = 'utf8'添加到您的$PROFILE,但请注意,这将影响会话中对具有-Encoding参数的cmdlet的所有调用,除非显式使用该参数;另请注意,在 Windows PowerShell 中,您总是会获得 * 带有BOM* 的UTF-8文件;相反,在 PowerShell [Core] v6+ 中,默认为BOM-* 减去 * UTF-8(在没有-Encoding和有-Encoding utf8的情况下,您都必须使用'utf8BOM'

可选背景信息

eryksun的所有输入进行提示。

***当TrueType字体处于活动状态时 ,控制台窗口 * 缓冲区 * 正确地保留(非ASCII)Unicode字符,即使它们没有 * 正确地;也就是说,正如eryksun所指出的,即使它们可能一般地 * 出现 * 为?,以指示当前字体缺乏支持,您也可以 * 复制&粘贴 * 这样的字符到其他地方而不会丢失信息。
*PowerShell能够将Unicode字符 * 输出到控制台 *,甚至无需先切换到代码页65001

然而,这本身并不保证其他程序可以正确处理这样的输出-见下文。

  • 当涉及到通过 stdout 与 * 外部程序 * 通信时(piping,则PowersShell使用在**$OutputEncoding首选项变量中指定的字符编码,该编码默认为ASCII(!),这意味着任何非ASCII字符都将转换为 literal?字符,从而导致 * 信息丢失 *。(相比之下,PowerShell Core(v6+)现在一致使用(无BOM)UTF-8作为默认编码。)
  • 然而,相比之下,传递非ASCII * 参数*(而不是stdout(管道)输出)到外部程序似乎 * 不需要 * 特殊的配置(我不清楚为什么这样做);例如,以下Node.js命令正确返回€: 1,即使使用默认配置:

node -pe "process.argv[1] + ': ' + process.argv[1].length" €

*[Console]::OutputEncoding

  • 控制控制台将程序输出转换为控制台显示字符时采用的字符编码。
  • 告诉 PowerShell 在从外部程序捕获输出 * 时采用什么编码**。

其结果是,如果需要从一个生成UTF-8的程序中 * 捕获输出 *,则还需要将[Console]::OutputEncoding设置为UTF-8;设置$OutputEncoding仅覆盖 * 输入 *(到外部程序)方面。

**[Console]::InputEncoding为控制台[2]中的 * 键盘输入 * 设置编码,并确定PowerShell's 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之前)上的问题

  • chcp的活动值65001中断了某些外部程序的控制台输出,甚至中断了Windows旧版本中的批处理文件,这可能最终源于WriteFile() Windows API函数中的错误(标准C库也使用),错误地报告了 * 字符 * 的数量,而不是 * 字节 * 的数量,代码页65001有效,如X1 E10 F1 X中所讨论的。
  • 根据bobince在2008年对this answer的评论,由此产生的症状是:“我的理解是,返回字节数的调用(如fread/fwrite/etc)实际上返回字符数。这会导致各种症状,如输入读取不完整、在fflush中挂起、批处理文件损坏等。”

本机Windows控制台(终端)conhost.exe的上级替代产品

eryksun建议了两个替代本机Windows控制台窗口conhost.exe)的方法,由于使用了现代的、GPU加速的DirectWrite/DirectX API,而不是“无法处理复杂脚本、非BMP字符或自动回退字体的旧GDI实现”,因此提供了更好、更快的Unicode字符 * 渲染

  • 微软自己的开源**Windows Terminal**,自Windows 10以来通过微软商店分发和更新-请参阅here的介绍。
  • 长期使用的第三方替代产品**ConEmu**,其优点是也可以在较旧的Windows版本上工作。

[1]请注意,从PowerShell会话 * 内部 * 运行chcp 65001是 * 无效 * 的,因为.NET * 在启动时缓存 * 控制台的输出编码,并且不知道后来使用chcp所做的更改(只拾取直接通过[console]::OutputEncoding]所做的更改)。
[2]我不清楚这在实践中如何体现;如果你知道,一定要告诉我们。

ffvjumwh

ffvjumwh2#

出于测试的目的,我创建了一些文件夹和文件,其中包含来自不同Unicode子范围的valid names,如下所示:

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

另一方面,使用SimSun控制台字体时,显示的是替换符号(可见性差),而不是阿拉伯语和希伯来语字符,而CJK字符似乎显示正确:

请注意,所有替换符号仅显示,而真实的字符将保留,如您在上面的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›
☺☻♥♦

为了完整起见,以下是Enable More Fonts for the Windows Command Prompt的相应注册表值(这也适用于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     : *新宋体
vhmi4jdf

vhmi4jdf3#

如果您安装Microsoft's "Windows Terminal" from the Microsoft Store(或预览版),它会预先配置为完全Unicode本地化。

你仍然不能 * 输入命令 * 与特殊字符...除非你使用WSL!😍

a64a0gku

a64a0gku4#

Powershell伊势是用于显示外来字符的选项: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]
e4yzc0pl

e4yzc0pl5#

我也面临着类似的挑战,使用亚马逊翻译服务。我从Windows商店安装了终端,它现在对我有效了!

xt0899hw

xt0899hw6#

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

xhv8bpkk

xhv8bpkk7#

刚刚注册只是为了澄清为什么“Lucida控制台”作为字体在Powershell伊势中工作的困惑.不幸的是,我无法评论,由于丢失的声誉,所以在这里作为答案:
在普通的Powershell中,所有字符都以配置的字体显示。这就是为什么中文或西里尔字符会被“Lucida Console”和许多其他字体打断。对于中文字符,Powershell伊势会自动将字体更改为“Dengxian”。
您可以通过将特殊字符复制到Word或能够显示不同字体的类似程序中,找到用于特殊字符的替代字体。

相关问题