如何在Windows 10上使用CSCRIPT与ANSI转义序列?

6
我正在尝试在Windows 10控制台中使用CSCRIPT(JScript)中可用的新VT100 ANSI转义序列功能。但是我无法让它正常工作。
这是一个非常简单的JScript脚本:

test.js

WScript.Echo('\x1B[7mReverse\x1B[0m Normal');
WScript.stdout.WriteLine('\x1B[7mReverse\x1B[0m Normal');

我已经进行了多次测试,发现 CSCRIPT 输出的转义序列直接写入屏幕时无效,只有在先写入文件并使用 TYPE 命令或者通过 FOR /F 捕获后使用 ECHO 命令才能生效。

enter image description here

我有两个问题:

1)为什么从CSCRIPT直接写入控制台不起作用?
2)如何使直接写入工作?

我想要在JREPL.BAT正则表达式查找/替换实用程序中添加文本高亮功能(因此使用了标签),但如果需要临时文件和/或FOR / F,则不会实现该功能。


你尝试通过“more”管道传输它或将其重定向到CON了吗? - jeb
@jeb - MORE测试已经在问题中了(没有帮助),我还尝试将其重定向到CON(未显示,仍然没有帮助)。我发现,如果我通过ANSICON运行测试,无论是通过管道到ANSICON,还是作为ANSICON命令行参数,或者作为命令行命令传递给ANSICON进程,它都可以正常工作。 - dbenham
2个回答

6

微软文档指出:

如果使用SetConsoleMode标志在屏幕缓冲区句柄上设置了ENABLE_VIRTUAL_TERMINAL_PROCESSING标志,下列终端序列将被拦截并写入输出流中。您可以使用GetConsoleMode和SetConsoleMode标志配置此行为。

因此,我编写了一个简单的C程序来更改控制台模式,然后作为管道或启动另一个进程并等待(抱歉,这只是测试代码)。

#define _WIN32_WINNT   0x0500
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004

int _tmain(int argc, TCHAR *argv[]){

    // Console handlers
    DWORD dwOldMode, dwMode ;
    HANDLE hStdout;

    // Pipe read buffer
    int c;

    // Spawn process variables
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    // Retrieve standard output handle
    hStdout = GetStdHandle( STD_OUTPUT_HANDLE );
    if (! GetConsoleMode( hStdout, &dwOldMode ) ) {
        return 1;
    }

    // Change standard output handle
    dwMode = dwOldMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    if (! SetConsoleMode( hStdout, dwMode ) ){
        CloseHandle( hStdout );
        return 2;
    }

    if( argc < 2 ) {
        // If there is not an argument, read stdin / write stdout 
        while ( EOF != (c = getchar()) ) putchar( c );    
    } else {
        // Argument is present, create a process and wait for it to end
        ZeroMemory( &si, sizeof(si) );
        si.cb = sizeof(si);
        ZeroMemory( &pi, sizeof(pi) );
        if( !CreateProcess(NULL, argv[1], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi )){
            printf( "CreateProcess failed (%d).\n", GetLastError() );
            return 3;
        }
        WaitForSingleObject( pi.hProcess, INFINITE );
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );    
    }

    // Restore old console mode
    SetConsoleMode( hStdout, dwOldMode );
    CloseHandle( hStdout );

    return 0;
};

使用 mingw/gcc 编译生成了 run.exe。结果如下:

测试会话的输出截图

现在,处理来自 cscriptfindstr 的输出并解释转义序列。

另外,如果我运行 cmd.exe 而不是单独运行这些程序

重定向 cmd.exe 的输出截图

由于我没有更改 findstr.execscript.execmd.exe 的代码,只是它们所工作的环境不同,因此看起来:

  • 既不是 cscript 也不是 findstr 配置/更改控制台缓冲区配置

  • 一些内部 cmd 命令更改了缓冲区配置(我忘记在截图中包含它了,但 copy test.txt conprompt 也可以工作),或者正如你所说,它们使用了不同的输出方法

  • 对于将数据写入标准输出流的应用程序,唯一的要求是正确配置控制台输出缓冲区模式。

不,我不知道如何通过纯批处理启用它。


1
耶!我终于在 SuperUser 的帖子中找到了一种方法,可以为 CSCRIPT(或任何不禁用它的控制台应用程序)启用 VT-100。请查看我的更新答案。 - dbenham
@dbenham,“是啊!!”,我必须同意你的观点。似乎保持它处于活动状态可能会有一些副作用,但这确实是一个很好的发现和探索新方法的方式。谢谢。 - MC ND

3

第二部分更新答案:如何在CSCRIPT中使其工作

我最终在这个SuperUser答案中找到了一个机制来启用CSCRIPT中的VT-100序列。我复制了答案中相关的文本,并将其贴在下面。

幸运的是,全局默认值可以从“选择加入”更改为“选择退出”。注册表键HKEY_CURRENT_USER\Console\VirtualTerminalLevel设置处理ANSI转义序列的全局默认行为。创建一个DWORD键(如果需要),并将其值设置为1以全局启用(或0以禁用)默认情况下的ANSI处理。

[HKEY_CURRENT_USER\Console]
"VirtualTerminalLevel"=dword:00000001

请注意,此注册表设置控制默认,这意味着它仅影响不通过调用SetConsoleMode(...)显式操作控制台模式的控制台应用程序。因此,尽管注册表值可能有助于为控制台模式忽略的应用程序启用ANSI,但对于任何控制台模式明智的应用程序(由于某种原因)可能显式禁用ANSI,它将没有任何影响。

请注意,更改仅影响新启动的控制台窗口-它不会为已存在的控制台窗口启用VT-100。


这个线程中,DosTips用户aGerman发现您可以通过异步启动PowerShell来启用转义序列。 PowerShell配置输出以支持转义序列,并且即使在PowerShell退出后,该支持也仍然存在,只要您的CSCRIPT进程保持活动状态。

例如,以下是一些JScript代码,可启用这些序列:

var ps = WScript.CreateObject("WScript.Shell").Exec("powershell.exe -nop -ep Bypass -c \"exit\"");
while (ps.Status == 0) WScript.Sleep(50);

关于第一部分的原始回答:为什么在CSCRIPT中不起作用

好的,我认为我有一个可行的理论来解释为什么它不能工作。我相信必须有某种低级别的方式 / 调用 / 函数 / 方法(无论是什么),只有几个内部命令知道如何将stdout传递到控制台。我基于以下事实得出这个结论:FINDSTR也无法向控制台发送可用的转义序列,如下所示:

enter image description here

我已经证明了 TYPE 和 ECHO 都可以工作。我还验证了 SET / P 可以工作(未显示)。因此,我怀疑 cmd.exe 已被修改以支持新的 Windows 10 控制台功能。

我很想看到一些微软的文档,描述要将转义序列发送到控制台所需的机制。


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