在Windows终端中启用ANSI序列。

3

我在Windows中遇到了一个有趣的问题:


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

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

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

(最后一行以品红色打印)

terminal screenshot

为什么会发生这种情况,我如何在不调用os.execute()的情况下使ANSI代码起作用?
1个回答

4

为了在控制台中使用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时,不会发生这种情况吗? - Igor Chubin
终端模式不是终端窗口的属性,而是控制台句柄的属性。父进程(cmd.exe)和子进程(lua.exe)通过不同的控制台句柄访问同一个终端窗口。当 os.execute 创建 cmd.exe 子进程时,控制台句柄被子进程继承,这是默认行为。但是当 cmd.exe 创建 lua.exe 子进程时,控制台句柄故意不被继承,以使子进程相互隔离(您的程序不依赖于在同一控制台中运行的先前程序)。 - Egor Skriptunoff
是的,看起来确实是这样,但仍然不清楚它如何知道在什么情况下必须创建新的控制台句柄以及它必须继承什么。或者cmd.exe是否总是为每个子进程创建一个新的控制台句柄? - Igor Chubin
cmd.exe deliberately prohibits console handle inheritance while creating subprocess. This is done by parameter bInheritHandles of WinAPI function CreateProcessW() - Egor Skriptunoff
@Tieske - 你在问什么代码?我回答中的代码需要LuaJIT,而不是Lua 5.x。 - undefined
显示剩余3条评论

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