Вывод цветного текста в консоли PowerShell с использованием кодов ANSI/VT100

Я написал программу, которая печатает строку, содержащую экранированные последовательности ANSI, чтобы сделать текст цветным. Но это не работает должным образом в консоли Windows 10 по умолчанию, как вы можете видеть на снимке экрана.

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

Как мне добиться, чтобы программа печатала цветной текст без каких-либо обходных путей?

введите описание изображения здесь

Это мой исходный код программы (Haskell), но язык не имеет значения, просто чтобы вы могли видеть, как написаны управляющие последовательности.

main = do
    let red = "\ESC[31m"
    let reset = "\ESC[39m"
    putStrLn $ red ++ "RED" ++ reset

person Community    schedule 03.08.2018    source источник


Ответы (3)


Хотя консольные окна в Windows 10 поддерживают управляющие последовательности VT (виртуальный терминал)/ANSI, в принципе, поддержка отключена по умолчанию .

У вас есть три варианта:

  • (a) Активировать поддержку глобально по умолчанию, постоянно через реестр, как описано в этот ответ SU.

    • In short: In registry key [HKEY_CURRENT_USER\Console], create or set the VirtualTerminalLevel DWORD value to 1
      • From PowerShell, you can do this programmatically as follows:
        Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1
      • От cmd.exe (также работает из PowerShell):
        reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1
    • Откройте новое окно консоли, чтобы изменения вступили в силу.
    • См. предостережения ниже.
  • (b) Активируйте поддержку изнутри вашей программы только для этой программы (процесса), вызвав функцию SetConsoleMode() Windows API.

    • See details below.
  • (c) Специальный обходной путь, из PowerShell: направлять выходные данные из внешних программ в Out-Host; например, .\test.exe | Out-Host

    • See details below.

Re (a):

Подход на основе реестра неизменно активирует поддержку VT глобально, то есть для всех окон консоли, независимо от того, какая оболочка/программа в них запущена:

  • Отдельные исполняемые файлы/оболочки могут по-прежнему деактивировать поддержку для себя, при желании, с помощью метода (b).

  • Однако, наоборот, это означает, что вывод любой программы, которая явно не контролирует поддержку VT, будет подвергаться интерпретации последовательностей VT; хотя в целом это желательно, гипотетически это может привести к неправильной интерпретации выходных данных программ, которые случайно создают выходные данные с последовательностями, подобными VT.

Примечание:

  • Хотя существует существует механизм, который позволяет ограничивать параметры окна консоли запускаемым исполняемым файлом/заголовком окна через подключи [HKEY_CURRENT_USR\Console], значение VirtualTerminalLevel, похоже, там не поддерживается.

  • Однако даже если бы это было так, это не было бы надежным решением, поскольку открытие окна консоли с помощью файла ярлыка (*.lnk) (например, из меню «Пуск» или Bar) не будет учитывать эти настройки, потому что в *.lnk файлах встроены настройки; в то время как вы можете изменить эти встроенные настройки через диалоговое окно графического интерфейса Properties, на момент написания этой статьи параметр VirtualTerminalLevel не отображался в этом графическом интерфейсе.


Re (b):

Вызов функции SetConsoleMode() Windows API изнутри программы (процесса), как описано здесь, громоздко даже в C# (из-за необходимости объявления P/Invoke) и может не быть опцией:

  • для программ, написанных на языках, из которых вызов Windows API не поддерживается.

  • если у вас есть уже существующий исполняемый файл, который вы не можете изменить.

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


Re (c):

PowerShell автоматически активирует поддержку VT (виртуального терминала) для себя при запуске (в последних выпусках Windows 10 это относится как к Windows PowerShell, так и к PowerShell Core), но это не распространяется на внешние программы, вызываемые из PowerShell.

Однако если вы ретранслируете вывод внешней программы через PowerShell, последовательности VT распознаются; использование Out-Host — самый простой способ сделать это (Write-Host тоже подойдет):

.\t.exe | Out-Host

Примечание. Используйте Out-Host, только если вы хотите печатать на консоль; если, напротив, вы хотите захватить вывод внешней программы, используйте только $capturedOutput = .\test.exe

Предупреждение о кодировке символов: Windows PowerShell по умолчанию ожидает, что выходные данные внешних программ будут использовать кодовую страницу OEM, как определено устаревшей локалью системы (например, 437 в англо-американских системах) и как отражено в [console]::OutputEncoding . Консольные программы .NET автоматически учитывают этот параметр, но для программ, отличных от .NET (например, скриптов Python), которые используют другую кодировку (и производят не только чистый вывод ASCII (в 7-битном диапазоне)), вы должны (как минимум временно) укажите эту кодировку, присвоив [console]::OutputEncoding; например, для UTF-8:
[console]::OutputEncoding = [Text.Encoding]::Utf8.
Обратите внимание, что это необходимо не только для обхода VT-последовательностей, но и для правильного отображения символов, отличных от ASCII, в PowerShell. .

PowerShell Core, к сожалению, начиная с версии 6.1.0-preview.4 по умолчанию также использует кодовую страницу OEM, но это следует рассматривать как ошибку, учитывая, что PowerShell Core по умолчанию использует UTF-8 без BOM.

person mklement0    schedule 04.08.2018

Спасибо пользователю mklement0 за то, что он сообщил мне, что поддержка VT не включается автоматически. Это заставило меня посмотреть в правильном направлении, и я нашел этот полезный пост.

Итак, чтобы ответить на мой вопрос: добавьте или установите раздел реестра

HKCU:\Console  -  [DWORD] VirtualTerminalLevel = 1

Перезапускаю консоль и все работает.

person Community    schedule 04.08.2018
comment
Спасибо за копание глубже - мне даже не приходило в голову, что может быть глобальный переключатель, но это имеет смысл. Я позволил себе включить ваши выводы в свой обновленный ответ, включая программные способы переключения выключателя и связанные с ними предостережения, чтобы дать исчерпывающий ответ. - person mklement0; 05.08.2018

В частности, для Haskell вам нужно вызвать hSupportsANSIWithoutEmulation, чтобы включить управляющие последовательности ANSI в вашей программе в Windows 10.

person Steve    schedule 16.04.2021