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

28

最新的Windows 10更新中,conhost.exe已经支持ANSI转义序列

enter image description here

我已经确认cmd.exe正确地捕获了转义序列,所以我已经进行了必要的更新。特别地,我尝试输入 prompt $e[?25l 隐藏光标,然后输入 prompt $e[?25h 再次显示光标。

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

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

嗯,光标没有隐藏。我该如何设置才能使控制台正确接收Python的转义序列?

4个回答

41

问题在于Python解释器不支持处理ANSI转义序列。在Windows命令提示符中,ANSI序列可以正常工作,因为cmd启用了它们。如果您从命令提示符中启动Python,则会发现ANSI序列可以正常工作,包括启用和禁用光标的序列。这是因为cmd已经为该控制台窗口启用了它们。

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

另一个更难的解决办法是使用ctypes通过调用具有设置ENABLE_VIRTUAL_TERMINAL_PROCESSING标志的SetConsoleMode Windows API来为控制台窗口启用ANSI转义序列处理。例如:

import ctypes

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

@user89 不幸的是,没有与O_NONBLOCK标志等效的选项。Windows使用不同的模型,涉及在句柄上等待事件和重叠I/O(后者不支持控制台句柄)。您需要通过ctypes深入了解Windows API,或让类似curses的东西为您处理它。Python for Windows未提供Curses支持,但您可以单独下载端口,请参见:https://docs.python.org/3.3/howto/curses.html - Ross Ridge
有道理 - 我想我只能等待看看Windows控制台如何改进(特别是随着即将发布的Windows上的bash),现在还是使用Linux虚拟机来运行和开发控制台应用程序。 - bzm3r
@RossRidge 对不起,我很无知,请问为什么是数字“7”? - Henrique Dias
7
这句话的意思是:这相当于 ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_VIRTUAL_TERMINAL_PROCESSING,它等同于 0x0001|0x0002|0x0004,它等于7。Python无法包含定义这些标志的C头文件,因此我只是用它们最终评估为7的数字进行了替换。前两个标志默认为开启状态,启用您所期望的正常控制台输出处理,第三个标志是启用ANSI转义序列处理的标志,默认情况下未启用。 - Ross Ridge
@RossRidge 非常感谢!我一直在尝试用Go做和你一样的事情,然后我发现了这篇文章和你的回答。然后我尝试了7,它起作用了(之前我一直在尝试0x0004)。我只是不知道为什么是7。谢谢 :) - Henrique Dias
显示剩余5条评论

5

我提出的一些代码在这里的改编应该能帮助您入门。它在Windows 10上启用了ANSI VT模式(虚拟终端处理)。将参数值1传递给stdout2stderr

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

1
它不起作用,显示“NameError:未定义名称'compat_ctypes_WINFUNCTYPE'” - barteks2x
1
正如作者所提到的那样,这段代码来自于关于youtube-dl的GitHub问题评论。缺失的函数在youtube-dl中已经定义了(你可以使用GitHub搜索功能在其中找到它)。如果你没有使用PyPy,那么 compat_ctypes_WINFUNCTYPE = WINFUNCTYPE ,所以你可以相应地替换这些调用。 - mhucka

4

Colorama包可以在Windows中启用ANSI代码。

用法:

from colorama import init
init()

然后 ANSI 码应该可以正常工作。


1
很棒的答案!我之前用过colorama,但不知道init()可以在Windows上启用支持。 - simpleuser

3

请使用此处建议的解决方案:https://dev59.com/U3VC5IYBdhLWcg3wcw0m#293633

运行:

os.system('color')

要在终端上启用彩色模式。

我一直使用ctypes的被接受的答案,直到我发现了这个。这个解决方案不需要任何额外的安装或本地API诡计。

这个命令不是一个可执行文件,而是Windows cmd终端中的特定命令。

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/color

它的正式用途是更改外壳的前景/背景颜色。它将激活ANSI/VT100的事实显然没有记录。


谢谢。这是一个非常简单的解决方案,在Win10 22H2中适用于cmd和powershell,运行良好。 - unityJarvis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接