65.9K
CodeProject 正在变化。 阅读更多。
Home

为批处理文件添加颜色

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (31投票s)

2007年1月5日

CPOL

4分钟阅读

viewsIcon

371509

downloadIcon

10362

一个增强的带颜色支持的 ECHO 命令行实用程序。

Colorize Batch File

引言

批处理文件对于处理重复性任务非常有用。但是,它们缺少一些用户友好的功能,例如彩色控制台输出。彩色输出可能有助于吸引对重要信息的注意。Win32 API 提供了一些有用的函数来与控制台进行交互(请参阅 MSDN 中的 Console Functions)。但在您的批处理文件中,唯一可用的命令是 COLORCOLOR 命令仅定义整个窗口控制台的颜色。

cecho 是一个增强的带颜色支持的 ECHO 命令行实用程序,灵感来自 Dennis Bareis 的 CTEXT 实用程序

最后一节解释了如何使用 Debug.exe 程序(直到 Windows Vista)将 cecho 实用程序嵌入到批处理文件中。

Using the Code

cecho 在解析颜色信息后,简单地将命令参数重定向到标准输出。cecho 参数包括:

  • {XX}:颜色代码,由两个十六进制数字组成。例如,{0A} 浅绿色
  • {color}:易于理解的文本形式的颜色信息。例如,{light red on black}
  • {\n \t}:换行符 - 制表符。
  • {\u0000}:Unicode 字符代码。
  • {{:转义字符 '{'。
  • {#}:恢复初始颜色。

可用颜色

0 = 黑色 8 = 灰色
1 = 藏青色 9 = 蓝色
2 = 绿色 A = 亮绿色
3 = 青色 B = 水色
4 = 栗色 C = 红色
5 = 紫色 D = 洋红色
6 = 橄榄色 E = 黄色
7 = 银色 F = 白色

示例批处理文件

@echo off

cecho {0C}This line is red{#}

REM Print ASCII char 0x07 (beep) 
cecho {\u07 \u07}

cecho This {black on blue}word{#} is black on a blue background

...

更改日志:在 cecho v2.0 中,{0x00} ASCII 代码已被 {\u0000} Unicode 字符替换。

cecho 内部

彩色控制台输出

对控制台输出进行着色相当直接。只需调用 SetConsoleTextAttribute Win32 API 并传入正确的颜色代码。所有后续的控制台输出都将以指定的颜色显示。我们稍后会解释 tokStack

void setColor()
{
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    tokStack[tokCurs-1] = 0;
    SetConsoleTextAttribute(hConsole, (WORD)strtol(tokStack, 0, 16));
}

解析命令行参数

根据 cecho 文档,命令行参数的解析可以由以下解析树描述:

Parse Tree

图 1:解析树

C 语言中的解析树语法变为:

#define MAX_TRANS 5

// expected tokens list
wchar_t* Grammar[][MAX_TRANS] = {
    {L"{", L""},
    {L"{}", L"\\", L"0123456789abcdefABCDEF", L" \t", L""},
    {L"tn", L"u"},
    {L"0123456789abcdefABCDEF"},
    {L"0123456789abcdefABCDEF", L" \t", L"}", L"\\"},
    {L"0123456789abcdefABCDEF", L""},
    {L"}", L""},
    {L"}", L""}
};

// action executed on state changed
Fct Action[][MAX_TRANS] = {
    {&printStack,0},
    {&resetStack,0,0,0,0},
    {&printEscChar,0},
    {0},
    {0,&printHexChar,&printHexChar,&printHexChar},
    {0,0},
    {&setColor,0},
    {&parseColor,0}
};

// next state Ids
char Successor[][MAX_TRANS] = {
    {1,0},
    {0,2,5,1,7},
    {1,3},
    {4},
    {4,1,0,2},
    {6,7},
    {0,7},
    {0,7}
};

解析器状态图包含 7 个状态。Grammar 数组存储预期标记的列表,这些标记会触发特定状态的转换。空列表充当通配符标记。Action 数组维护指向在特定转换期间执行的函数的指针。Successor 数组标识每个转换的下一个状态。

解析器引擎会遍历所有输入字符。如果字符触发了当前解析器状态的传出转换,则该字符将被添加到 tokStack 标记堆栈中,执行与转换关联的操作(如果有),然后更新解析器状态。

// token stack
wchar_t tokStack[MAX_STACK];
// token stack cursor
int tokCurs = 0;

int wmain(int argc, wchar_t* argv[])
{
    short trans, clr, fired;
    wchar_t* token;
    char state = 0;

    [...]

    // retrieve command argument
    LPWSTR input = GetCommandLineW();
    
    if (*input == L'"')
        input += 2;

    input += wcslen(argv[0]) + 1;

    // parse input string
    for (fired = 0, token = input; *token != 0; token++)
    {
        // does token trigger a transition ?
        for (trans = 0; trans < MAX_TRANS && Grammar[state][trans] != 0; trans++)
        {
            // check if token belongs to the expected tokens list ? 
            // a empty list acts as a wildchar token
            if (wcschr(Grammar[state][trans], *token) || *Grammar[state][trans] == 0)
            {
                // push token into the stack
                if (tokCurs < MAX_STACK)
                    tokStack[tokCurs++] = *token;

                // execute the action associated to the transition (if any)
                if (Action[state][trans] != 0)
                {
                    (*Action[state][trans])();
                    tokCurs = 0; // reset token stack
                }
                // update parser state
                state = Successor[state][trans];
                fired = 1;
                break;
            }
        }
        if (fired == 0)
        {
            wprintf(L"Syntax error: '%c' col %i", *token, input - token);
            return -1;
        }
    }
    // print remaing token stack
    tokCurs++;
    printStack();
    return 0;
}

Action 中的函数非常简单,不值得在此进行评论。

附录:如何将 cecho.exe 嵌入到批处理文件中

注意Debug.exe 已在 Windows 7 中淘汰。我保留此部分仅供参考,但不再推荐使用它。

嵌入机制依赖于 Debug.exe 程序。cecho 二进制文件使用 Anthony Caruso 的 BIN2DBG.EXE 工具转换为调试脚本。然后将调试脚本嵌入到批处理文件中。在运行时,Debug.exe 会处理调试脚本以重新创建 cecho 可执行文件。

  1. 使用 BIN2DBG.EXEcecho.exe 转换为 cecho.dbg
  2. C:\>bin2dbg.exe cecho.exe
  3. 使用文本编辑器编辑 cecho.dbg。修改 cecho.dbg 第一行的文件扩展名,因为 Debug.exe 不允许写入 EXE 文件。例如,使用 *.com 扩展名。
  4. n cecho.com
    
    e 100 4D 5A 90 0 3 0 0 0 4 0 0 0 FF FF 0 0 
    e 110 B8 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 
    e 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    ...
  5. cecho.dbg 包含到您的批处理文件中。您的批处理文件必须完全重建相同的 cecho.dbg 文件。可以使用批处理命令
  6. C:\>for /f "delims=" %l in (cecho.dbg) do (echo echo %l ^>^> tmp.dbg >> cecho.bat)
  7. cecho.bat 的末尾添加命令以重新构建 cecho.com(以前是 cecho.exe)。
  8. ...
    echo e 10E0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  >> tmp.dbg
    echo e 10F0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  >> tmp.dbg
    echo e 1100 0  >> tmp.dbg
    echo rcx >> tmp.dbg
    echo 1000 >> tmp.dbg
    echo w >> tmp.dbg
    echo q >> tmp.dbg
    
    echo debug ^< tmp.dbg > tmp.bat
    echo exit >> tmp.bat
    
    start /wait /min tmp.bat
    
    del tmp.bat
    del tmp.dbg

    tmp.bat 文件和 start /wait /min 命令的技巧由 Jakob Bohm 在本文的论坛部分详细介绍:Possible reason for the start /min /wait trick。它基本上是在一个单独的控制台中运行 16 位 DOS Debug.exe,以防止文件句柄保持打开状态。

  9. 我们完成了!cecho 现在可用,并准备好为您的批处理输出添加颜色。
  10. ...
    call :unpack_cecho
    
    REM cecho.com is now available for use.
    cecho.com {red}This line in red{#}
    ...
    del cecho.com
    goto :eof

由于调试脚本的大小比 EXE 文件大得多,我们需要尽可能减小 cecho.exe 的尺寸。 UPX 可执行文件打包器将 cecho.exe(使用 VC++10 编译)减小了 50%。

我还建议将调试脚本作为“函数”包含在您的批处理文件中。请参阅 Ritchie Laurence 的批处理函数语法

历史

  • 初始发布 - 2007 年 1 月 3 日
  • 次要更改 - 2007 年 8 月 7 日
    • 添加了“start /min /wait”技巧,并使用 UPX 减小了 EXE 尺寸。
  • cecho v2.0 - 2010 年 8 月 23 日
    • 添加了 Unicode 支持。
    • 添加了 x64 支持。
© . All rights reserved.