【问题标题】:Enable ANSI sequences in windows terminal在 Windows 终端中启用 ANSI 序列
【发布时间】:2021-03-03 06:31:33
【问题描述】:

我偶然发现了一个有趣的问题 在 Windows 中:


C:\> lua
> print("\x1b[95mMagenta\x1b[0m")
-[95mMagenta-[0m

但是如果我运行os.execute(),即使是一个空的命令, 在它之前,它按预期工作:

C:\> lua
> os.system(""); print("\x1b[95mMagenta\x1b[0m")
Magenta

(最后一行用洋红色打印)

为什么会发生这种情况,以及如何使 ANSI 代码正常工作 不打电话给os.execute()

【问题讨论】:

    标签: windows terminal lua ansi


    【解决方案1】:

    要使 ANSI 代码在控制台中工作,您应该通过调用 WinAPI 函数 SetConsoleMode 来设置特定的 ENABLE_VIRTUAL_TERMINAL_PROCESSING 模式。

    当你在 Lua 中调用 os.execute() 时,Lua 会调用 C 运行时函数 system(),它会创建 cmd.exe 进程,该进程会初始化所有的花里胡哨。
    但是 Lua 当然不知道 Windows 控制台功能。它只适用于具有默认设置的控制台。


    更新:
    这是一个关于如何从 LuaJIT 脚本打开 ANSI 转义序列的示例:

    local ffi = require"ffi"
    ffi.cdef[[
    typedef int BOOL;
    static const int INVALID_HANDLE_VALUE               = -1;
    static const int STD_OUTPUT_HANDLE                  = -11;
    static const int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
    intptr_t GetStdHandle(int nStdHandle);
    BOOL GetConsoleMode(intptr_t hConsoleHandle, int* lpMode);
    BOOL SetConsoleMode(intptr_t hConsoleHandle, int dwMode);
    ]]
    local console_handle = ffi.C.GetStdHandle(ffi.C.STD_OUTPUT_HANDLE)
    assert(console_handle ~= ffi.C.INVALID_HANDLE_VALUE)
    local prev_console_mode = ffi.new"int[1]"
    assert(ffi.C.GetConsoleMode(console_handle, prev_console_mode) ~= 0, "This script must be run from a console application")
    local function turn_VT(on_off)
       assert(ffi.C.SetConsoleMode(console_handle, bit.bor(prev_console_mode[0], on_off and ffi.C.ENABLE_VIRTUAL_TERMINAL_PROCESSING or 0)) ~= 0)
    end
    
    print('\x1b[95mMagenta\x1b[m')
    turn_VT(true)
    print('\x1b[95mMagenta\x1b[m')
    turn_VT(false)
    print('\x1b[95mMagenta\x1b[m')
    

    【讨论】:

    • 是的,这听起来很合理;你的意思是它改变了当前终端的状态,即使在进程完成后终端的状态也改变了?同时,在这个终端上启动cmd.exe时不会出现这种情况?
    • 终端模式不是终端窗口的属性。它是控制台句柄的属性。父 (cmd.exe) 和子 (lua.exe) 进程通过不同的控制台句柄访问相同的终端窗口。当os.execute 创建cmd.exe 子进程时,控制台句柄由子进程继承,这是默认行为。但是当cmd.exe创建lua.exe子进程时,控制台句柄没有被继承,故意将子进程相互隔离(你的程序不依赖于之前在同一个控制台中运行的程序)。
    • 是的,看起来确实是这样,但是仍然不清楚它是如何知道在什么情况下必须创建一个新的控制台句柄以及它必须继承什么。还是 cmd.exe 总是为每个子进程创建一个新的控制台句柄?
    • cmd.exe 在创建子进程时故意禁止控制台句柄继承。这是由WinAPI函数CreateProcessW()的参数bInheritHandles完成的
    猜你喜欢
    • 2021-01-01
    • 2018-03-20
    • 2014-09-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    相关资源
    最近更新 更多