在Windows终端启用ANSI序列

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


C:\> lua
> print("\x1b[95m洋红色\x1b[0m")
-[95m洋红色-[0m

但如果我使用空命令在os.execute()之前运行它, 它会像预期的那样工作:

C:\> lua
> os.system(""); print("\x1b[95m洋红色\x1b[0m")
洋红色

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

终端屏幕截图

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

点赞
用户1847592
用户1847592

在控制台中使用 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[95m紫色\x1b[m')
turn_VT(true)
print('\x1b[95m紫色\x1b[m')
turn_VT(false)
print('\x1b[95m紫色\x1b[m')
2020-11-19 21:57:47