如何在 Windows 10 控制台中使用对 ANSI 转义序列的新支持?

Posted

技术标签:

【中文标题】如何在 Windows 10 控制台中使用对 ANSI 转义序列的新支持?【英文标题】:How to use the new support for ANSI escape sequences in the Windows 10 console? 【发布时间】:2016-08-14 02:17:36 【问题描述】:

最新的 Windows 10 更新包括 conhost.exe 中的support for ANSI escape sequences。

我已经能够确认转义序列在 cmd.exe 中被正确提取,所以我有必要的更新。特别是,我尝试输入prompt $e[?25l,隐藏光标,然后输入prompt $e[?25h,再次显示光标。

但是,如果我启动一个 Python 解释器,然后执行以下操作:

>>> import sys
>>> sys.stdout.write("\033[?25l")

嗯,光标没有隐藏。如何以正确的方式进行设置,以便控制台能够从 Python 获取转义序列?

【问题讨论】:

【参考方案1】:

问题在于 Python 解释器无法处理 ANSI 转义序列。 ANSI 序列在 Windows 命令提示符下工作,因为cmd 确实启用了它们。如果您从命令提示符启动 Python,您会发现 ANSI 序列确实有效,包括用于启用和禁用光标的序列。那是因为cmd 已经为该控制台窗口启用了它们。

如果您想要一些东西,您可以单击以启动启用了 ANSI 转义的 Python 解释器,您可以创建一个快捷方式来运行类似 cmd /c C:\PythonXY\python 的命令。

另一个更难的解决方案是使用 ctypes 来启用控制台窗口的 ANSI 转义序列处理,方法是调用带有 ENABLE_VIRTUAL_TERMINAL_PROCESSING 标志集的 SetConsoleMode Windows API。例如:

import ctypes

kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)

【讨论】:

嗨罗斯——感谢这个非常好的答案:) 看来你做了很多与控制台相关的事情,我想知道——你是否也知道如何在Windows 控制台,类似于在 Linux 中的操作,通过使用 O_NONBLOCK 标志? Python Prompt Toolkit 通过使用 Windows API 公开的特殊读取函数实现非阻塞输入:github.com/jonathanslenders/python-prompt-toolkit/blob/master/…——似乎没有我可以设置的标志,然后允许sys.stdin.read() 不阻塞? @user89 不,不幸的是,没有等效于 O_NONBLOCK 标志。 Windows 使用不同的模型,涉及等待句柄和重叠 I/O 上的事件(控制台句柄不支持后者)。您要么需要通过 ctypes 深入研究 Windows API,要么让诸如 curses 之类的东西为您处理它。用于 Windows 的 Python 不附带诅咒支持,但您可以单独下载端口,请参阅:docs.python.org/3.3/howto/curses.html 有道理——我想我会等着看 Windows 控制台如何改进(尤其是随着即将在 Windows 上发布的 bash),现在坚持在 Linux 上使用 VM,以便运行+开发控制台应用程序。 @user89 Bash for Windows 是他们用于 Windows 的新 Linux 子系统的一部分。不幸的是,它的集成度比在 Linux 上使用 VM 好一点,文件系统是共享的,仅此而已。您可能会改用 Cygwin,它除了让您从 bash 运行 Windows 程序和从 Windows 命令行运行“Linux”程序外,它还可以在当前版本的 Windows 10 以及旧版本的 Windows 上运行。 @HenriqueDias 相当于ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_VIRTUAL_TERMINAL_PROCESSING 相当于0x0001|0x0002|0x0004 等于7。Python不能包含定义这些标志的C头文件,所以我只是替换了他们最终评估为第 7 位。前两个标志默认打开并启用您所期望的正常控制台输出处理,第三个是启用处理 ANSI 转义序列的标志,默认情况下未启用。【参考方案2】:

我提出的一些代码的改编 here 应该可以帮助您入门。在 Windows 10 上启用 ANSI VT 模式(虚拟终端处理)。为 stdout2 stderr 传入参数值 1

def _windows_enable_ANSI(std_id):
    """Enable Windows 10 cmd.exe ANSI VT Virtual Terminal Processing."""
    from ctypes import byref, POINTER, windll, WINFUNCTYPE
    from ctypes.wintypes import BOOL, DWORD, HANDLE

    GetStdHandle = WINFUNCTYPE(
        HANDLE,
        DWORD)(('GetStdHandle', windll.kernel32))

    GetFileType = WINFUNCTYPE(
        DWORD,
        HANDLE)(('GetFileType', windll.kernel32))

    GetConsoleMode = WINFUNCTYPE(
        BOOL,
        HANDLE,
        POINTER(DWORD))(('GetConsoleMode', windll.kernel32))

    SetConsoleMode = WINFUNCTYPE(
        BOOL,
        HANDLE,
        DWORD)(('SetConsoleMode', windll.kernel32))

    if std_id == 1:       # stdout
        h = GetStdHandle(-11)
    elif std_id == 2:     # stderr
        h = GetStdHandle(-12)
    else:
        return False

    if h is None or h == HANDLE(-1):
        return False

    FILE_TYPE_CHAR = 0x0002
    if (GetFileType(h) & 3) != FILE_TYPE_CHAR:
        return False

    mode = DWORD()
    if not GetConsoleMode(h, byref(mode)):
        return False

    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
    if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
        SetConsoleMode(h, mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
    return True

【讨论】:

它不起作用,给出“NameError: name 'compat_ctypes_WINFUNCTYPE' is not defined” 正如作者所说,这段代码来自关于 youtube-dl 的 GitHub 问题评论。缺少的功能在 youtube-dl 中定义(您可以使用 GitHub 搜索功能在那里找到它)。如果你没有使用 PyPy,那么 compat_ctypes_WINFUNCTYPE = WINFUNCTYPE 这样你就可以相应地替换这些调用。【参考方案3】:

colorama 包在 Windows 中启用 ANSI 代码。

用法:

from colorama import init
init()

在那之后 ANSI 代码应该可以工作了。

【讨论】:

很好的答案!我用过 colorama,但不知道 init() 会在 Windows 上启用支持。

以上是关于如何在 Windows 10 控制台中使用对 ANSI 转义序列的新支持?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 USB 声卡在 Windows 10 上启用 android 模拟器声音?

如何正确设置 PhpStorm? Сan't run PHPUnit on Laravel 8 on Windows 10. 调用未定义函数 Tests\Unit\is_id_term()

如何在Windows 10控制台中使用ANSI转义序列的新支持?

如何创建 Windows 10 设置应用程序(沉浸式控制面板)小程序?

windows10 在conda环境中使用ffmpeg对视频抽帧

windows10 在conda环境中使用ffmpeg对视频抽帧