Отображение Unicode в Powershell

То, чего я пытаюсь достичь, довольно просто, хотя Powershell делает это практически невозможным.

Я хочу отобразить полный путь к файлам, некоторые с арабскими, китайскими, японскими и русскими символами в именах

Я всегда получаю какой-то непонятный вывод, такой как показанный ниже enter image description here

Вывод, видимый в консоли, используется другим скриптом как есть. Выход содержит? вместо реальных персонажей.

Выполненная команда

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

Есть ли какой-нибудь простой способ запустить powershell (через командную строку или любым другим способом, который может быть записан в скрипт), чтобы вывод был виден правильно.

PS Я прошел через много похожих вопросов о переполнении стека, но ни у одного из них не было большого вклада, кроме как назвать это проблемой консольной подсистемы Windows.

Ответ 1

Перспектива PowerShell Core (см. следующий раздел для Windows PowerShell):

  • На Unix-подобных платформах PowerShell Core по умолчанию поддерживает UaT-8 (обычно в наши дни, учитывая, что современные Unix-подобные платформы используют локали на основе UTF-8).

  • В Windows именно языковой стандарт системы через ее кодовую страницу OEM определяет кодировку по умолчанию во всех консолях, включая окна консоли Windows PowerShell и PowerShell Core, хотя и последние версии Windows 10 теперь позволяет установить системный языковой стандарт для кодовой страницы 65001 (UTF-8) (эта функция все еще находится в бета-версии на момент выпуска Windows 10 версии 1903).

    • Если вы используете эту функцию, Windows 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.
      В Википедии приведен список шрифтов Windows, предназначенных для сценариев (алфавитов).

    • Чтобы изменить шрифт, щелкните значок в верхнем левом углу окна и выберите Properties, затем перейдите на вкладку Fonts и выберите интересующий шрифт TrueType.

  • Кроме того:

    • Кодовая страница окна консоли должна быть переключена на 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 теперь позволяют устанавливать системный языковой стандарт на кодовую страницу 65001 (UTF-8) (эта функция все еще находится в стадии бета-версии на момент выпуска Windows 10 версии 1903), что делает все окна консоли по умолчанию используется UTF-8, включая Windows PowerShell.
Если вы используете эту функцию, настройка [console]::InputEncoding/[console]::OutputEncoding больше не является строго необходимой, но вам все равно придется установить $OutputEncoding (что не обязательно в PowerShell Core, где $OutputEncoding по умолчанию уже имеет значение UTF -8).

Важно:

  • Эти настройки предполагают, что любые внешние утилиты, с которыми вы общаетесь, ожидают вход с кодировкой UTF-8 и производят вывод UTF-8.
    • К примеру, CLI, написанные на Node.js, соответствуют этому критерию.
    • Скрипты Python - если они написаны с поддержкой UTF-8 - тоже могут обрабатывать UTF-8.
  • Напротив, эти настройки могут ломать (более старые) утилиты, которые ожидают только однобайтовую кодировку, как подразумевается в устаревшей кодовой странице OEM системы.
    • До Windows 8.1 это включало даже стандартные утилиты Windows, такие как find.exe и findstr.exe, которые были исправлены в Windows 10.
    • В нижней части этого поста рассказывается, как обойти эту проблему, временно переключившись на UTF-8, по требованию для вызова данной утилиты.

Дополнительная справочная информация

Tip of the hat to eryksun for all his input.

  • Когда шрифт TrueType активен, буфер окна консоли правильно сохраняет (не ASCII) символы Юникода. даже если они не отображаются правильно; то есть, даже если они могут отображаться в общем виде как ?, что указывает на отсутствие поддержки текущего шрифта, вы можете скопировать & вставляйте такие символы в другое место без потери информации, как заметил Эриксун.

  • PowerShell способен выводить символы Unicode на консоль, даже не переключившись на кодовую страницу 65001 сначала.
    Однако это само по себе не гарантирует, что другие программы могут правильно обрабатывать такой вывод - см. ниже.

  • Когда речь идет о взаимодействии с внешними программами через stdout (piping), он использует кодировку символов, указанную в предпочтительной переменной $OutputEncoding, которая по умолчанию ASCII (!) в Windows PowerShell, что означает, что любые символы, не входящие в ASCII, транслитерируются в буквальные символы ?, что приводит к потере информации. (В отличие от этого, похвально, что PowerShell Core теперь использует (без спецификации) UTF-8 в качестве кодировки по умолчанию, повсеместно.)

    • В отличие от этого, однако, передача не-ASCII-аргументов (а не вывод stdout (piped)) внешним программам, похоже, не требует специальной настройки (мне непонятно, почему это работает); например, следующая команда 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]

  • Если переключение консоли на 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, в конечном итоге могло произойти из-за ошибки в функции API Windows WriteFile() (также используемой стандартная библиотека C), которая по ошибке сообщала о количестве символов, а не байтов с действующей кодовой страницей 65001, как обсуждалось в этом сообщении в блоге.

    • В соответствии с комментарием bobince к этому ответу от 2008 года, следующие симптомы: "Насколько я понимаю, вызовы, возвращающие количество байтов (например, fread/fwrite)/etc) фактически возвращает количество символов. Это вызывает широкий спектр симптомов, таких как неполное чтение ввода, зависание в fflush, сломанные пакетные файлы и т.д. "


Превосходные альтернативы родной консоли Windows (терминал), conhost.exe

eryksun предлагает две альтернативы родным консольным окнам Windows (conhost.exe), которые предоставляют более качественную и быструю визуализацию символов Unicode благодаря использованию современного API-интерфейса DirectWrite/DirectX с GPU-ускорением вместо "старой реализации GDI [которая] не может обрабатывать сложные сценарии, символы не-BMP или автоматические резервные шрифты".

  • Собственный Microsoft с открытым исходным кодом, Windows Terminal с открытым исходным кодом, который будет распространяться и обновляться через Магазин Microsoft в Windows 10 - ознакомьтесь с здесь.

  • Давняя сторонняя альтернатива ConEmu, которая также имеет преимущество работы с более старыми версиями Windows.


[1] Note that running [TG436] from inside a PowerShell session is not effective, because .NET caches the console output encoding on startup and is unaware of later changes made with [TG437] (only changes made directly via [TG438] are picked up).

[2] I am unclear on how that manifests in practice; do tell us, if you know.

Ответ 2

Разработал ответ Александра Мартина. Для тестирования я создал несколько папок и файлов с допустимыми именами из разных поддиапазонов Юникода следующим образом:

valid names

Например, при использовании шрифта консоли Courier New вместо символов CJK в консоли PowerShell отображаются символы замены:

Courier New

С другой стороны, при использовании шрифта консоли SimSun (плохо видимые) символы замены отображаются вместо символов арабского языка и иврита, в то время как символы 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 (это также работает для консоли 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     : *新宋体

Ответ 3

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]

Ответ 4

Убедитесь, что у вас есть шрифт, содержащий все проблемные символы, установленные и установленные в качестве шрифта консоли Win32. Если я правильно помню, щелкните значок PowerShell в верхнем левом углу окна и выберите "Свойства". В появившемся всплывающем диалоговом окне должна быть опция для установки используемого шрифта. Возможно, это должен быть шрифт растрового изображения (.FON или .FNT).

Ответ 5

Как насчет использования Powershell ISE?