如何在Windows 10上使用带有CSCRIPT的ANSI转义序列?

时间:2016-08-05 00:29:09

标签: batch-file windows-10 ansi-escape wsh

我正在尝试使用CSCRIPT(JScript)在Windows 10控制台中提供的新VT100 ANSI转义序列功能。但我无法让它发挥作用。

这是一个非常简单的JScript脚本:

test.js

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

我做了很多测试,CSCRIPT输出的转义序列直接写入屏幕时无效,只有先写入文件然后再打字才能工作,或者由FOR / F和ECHOed捕获

enter image description here

我有两个问题:

1)为什么直接写入控制台不能从CSCRIPT开始工作?
2)如何直接写作?

我想在我的JREPL.BAT regular expression find/replace utility(因此标记)中添加文字突出显示,但如果它需要临时文件和/或FOR /F,我将不会实现该功能。 p>

2 个答案:

答案 0 :(得分:6)

MS documentation

  

控制台主机拦截以下终端序列   当写入输出流时如果   使用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;
};

使用run.exe编译为mingw/gcc。结果是

Output capture of test session

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

另外,如果不是运行单独的程序,而是运行cmd.exe本身

Output capture of redirected cmd.exe

由于我没有更改findstr.execscript.execmd.exe的代码,只有他们正在使用它的环境似乎

  • cscriptfindstr都没有配置/更改控制台缓冲区配置

  • 一些内部cmd命令会更改缓冲区配置(我忘记将其包含在捕获中,但copy test.txt conprompt也可以工作)或者,正如您所指出的那样,使用不同的输出方法

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

不,我不知道如何从纯批次启用它。

答案 1 :(得分:3)

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

我终于找到了一种在this SuperUser answer的CSCRIPT中启用VT-100序列的机制。我复制了答案中的相关文字并将其发布在下面。

  

幸运的是,全局默认值可以从 opt-in 更改为 opt-out HKEY_CURRENT_USER\Console\VirtualTerminalLevel处的注册表项设置处理ANSI转义序列的全局默认行为。创建一个DWORD密钥(如果需要)并将其值设置为1以全局启用(或0禁用`)默认情况下ANSI处理。

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

请注意,此注册表设置控制默认,这意味着它仅影响未通过调用SetConsoleMode(...)显式操作控制台模式的控制台应用程序。因此,虽然注册表值可以帮助控制台模式 - 不经意应用程序启用 ANSI,但它对任何 console-mode-savvy <没有任何影响/ em> app(由于某种原因)可以明确禁用 ANSI。

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

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

例如,这里有一些将启用序列的JScript代码

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


我对第1部分的原始答案:为什么它在CSCRIPT中不起作用

好吧,我认为我有一个可行的理论,为什么它不起作用。我相信必须有一些低级的方式/调用/函数/方法(无论如何)将stdout传递给只有少数内部命令知道的控制台。我的理由是FINDSTR也无法向控制台发送正常的转义序列,如下所示:

enter image description here

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

我希望看到一些MS文档描述将转义序列发送到控制台所需的机制。