
不知道你是不是也遇到过这种情况写好的批处理脚本跑起来满屏黑底白字重要提示和报错全混在一起盯半天才找到关键那一行。其实cmd的文本颜色输出一直都有只是大多数人没认真折腾过。把这个玩明白以后你可以在批处理菜单里做彩色高亮、把日志按级别染成不同颜色、甚至输出256色和RGB真彩色界面友好度直接拉满写自动化脚本和服务器维护时也能一眼定位问题。这篇文章就把cmd彩色输出的所有玩法拆开讲清楚适合刚接触命令行的新手也适合想让脚本更好用的老手。1. 先从核心思路上说清楚cmd上色到底有几种路子1.1 默认界面为什么显得“土”cmd全称Command Prompt是Windows自带的命令行解释器。自打Windows NT时代起它默认就是黑底白字除非你手动去“属性”里改字体颜色和背景色否则所有输出都长一个样。早期这样设计倒也能用因为那时候终端里跑的都是纯字符程序也没有花里胡哨的需求。但现在不同了我们会在cmd里跑Python脚本、调用Git、执行扫描命令、看服务日志满屏字符没个重点效率确实提不上去。颜色输出的核心价值不是好看而是信息分层。同一段输出里路径、错误、成功提示、警告信息如果能用不同颜色区分人眼扫一遍就能定位问题。尤其是你写一个给同事用的批处理工具菜单选项用颜色区分出来别人操作起来也不会一脸懵。1.2 两种主流上色方案整窗换肤和局部染色在cmd里做颜色控制最常见的有两条路。一条是color命令它把整个窗口的前景色和背景色一起改属于“全局换肤”。优点是简单一条命令就搞定缺点是粗糙你没办法在输出中间只把某个词染红。另一条是ANSI转义序列也叫VT控制码。它通过向终端输出一段特殊的字符序列来临时改变后续文本的样式属于“局部染色”。你可以把某一行文字变成绿色下一行又恢复正常甚至在同一行内部混合颜色。Windows 10以后系统对被 Powershell、Linux 子系统使用的终端组件做了底层改造cmd也已经原生支持ANSI转义序列只是默认有些环境还需要手动打开。两条路不冲突实际项目里往往一起用color设置窗口整体风格ANSI序列用来做内容级的色彩标记。下面我把这两条路分别拆开讲清楚原理、写法和踩坑点。2. color命令五分钟上手整窗换肤2.1 颜色参数与16色调色板color命令的格式简单到不能再简单color 背景色前景色这里背景色和前景色都是十六进制数字字符取值范围0到F。也就是说命令后面跟两个字符就完成了全部设置。字符颜色字符颜色0黑色8灰色1蓝色9亮蓝色2绿色A亮绿色3湖蓝色B亮湖蓝色4红色C亮红色5紫色D亮紫色6黄色E亮黄色7白色F亮白色举个例子你想要经典的白字配蓝底就是color 1F想要绿字黑底就是color 0A想要亮红字黄底就是color 6C。我把这个命令放在批处理开头整个脚本执行期间窗口都保持这个配色效果立竿见影。注意color命令后面不带参数直接运行会把当前cmd窗口恢复成默认的黑底白字。这在脚本收尾阶段很有用避免你的工具“污染”用户原本设置的终端风格。2.2 别踩这两个坑颜色设置偶尔不生效、输出记录偏色第一有些程序会自己“覆盖”颜色。比如你在脚本里设置了color 0A然后调用某个第三方工具或Python脚本对方如果自己也输出ANSI转义序列颜色就会被临时改变。这不是color命令失灵而是内容级的染色优先级更高。你需要在调用完外部程序后再补一次color命令来恢复整体配色。第二当输出被重定向到文件时color只是改变cmd窗口的颜色不会给文件内容嵌入任何颜色标记。你如果做color 0A result.txt那文件里只会留下一个空行颜色并不会被“记录”进去。这种场景下想保留颜色信息只能靠下一节的ANSI转义序列或者换用支持颜色输出的日志框架。3. ANSI转义序列实现真正的局部彩色3.1 Windows为什么以前不支持ANSI很多人第一次接触ANSI转义序列是在Linux终端里觉得那是Unix系的事。其实Windows最早期的命令提示符也考虑过支持但后来图形化界面兴起微软很长时间都没有把终端的控制台规范对齐到Linux标准。导致的结果就是echo \033[31m红色\033[0m这类写法在Linux终端好使在win7及更老版本的cmd里只会原样输出一串乱码。Windows 10开始微软重新实现了控制台宿主底层引入了对ANSI转义序列的支持。不过有一个前提需要开启“虚拟终端处理”模式。正常情况下这个模式是默认开启的但某些精简版系统、远程桌面环境或特定的终端宿主设置会把它关掉。如果发现ANSI序列不生效大概率就是这里出了问题。打开方式很简单管理员权限打开cmd执行REG ADD HKCU\CONSOLE /v VirtualTerminalLevel /t REG_DWORD /d 1 /f然后重启cmd窗口。这里的VirtualTerminalLevel值为1表示开启VT处理值为0则是关闭。批处理脚本里可以用reg query先检查当前值再决定要不要写入。3.2 转义序列语法与效果对照ANSI转义序列虽然听着高端拆开看其实就是一串特殊字符ESC [ 控制参数 字母。ESC对应ASCII码27在文档里经常写成\x1b或者\033。在cmd窗口里一段完整的红色文字长这样echo ESC[31m这段文字是红色ESC[0m这里的31m表示设置前景色为红色0m表示重置所有样式为默认。实际输入时上面写的“ESC”并不是字母E、S、C而是真正的ESC转义字符。在批处理文件里写入这个字符有特定办法我后面专门讲。常用的SGR参数我整理成了表格对照着用就好。代码效果代码效果0重置所有样式90-97亮色前景色黑红绿黄蓝紫青白1加粗100-107亮色背景色4下划线30-37标准前景色黑红绿黄蓝紫青白7反显前景背景互换40-47标准背景色组合使用并不复杂比如想要黄色背景、红色文字并加粗可以写echo ESC[1;31;43m加粗红字黄底ESC[0m多个参数用分号分隔顺序不影响结果0m重置放最后就行。3.3 在批处理中写入真正的ESC字符这里是个大坑新手基本都会卡住。如果你直接在bat文件里打ESC[31m那里面其实是E、S、C三个字母cmd把它当成普通文本输出不是转义序列。你要插入的是ASCII码27对应的那个不可见字符。我常用的办法有三个方法一记事本软键盘。在记事本里按住Alt键不放在小键盘输入27松开Alt记事本里会插入一个不可见字符这就是ESC。复制进你的批处理文件就行。注意必须用小键盘数字笔记本需要先开NumLock。方法二先用PowerShell生成一个带ESC字符的脚本片段再复制到bat里。PowerShell里执行$esc [char]27 Set-Content -Path .\sample.bat -Value echo ${esc}[31m红色${esc}[0m -Encoding Default这段代码会生成一个bat文件里面自动包含了真实的ESC字符。方法三写一个独立的“输出工具”用C或Python生成带颜色的文本再让bat调用。这个方法最省心适合脚本里颜色特别多的情况。提示如果你用某些编辑器比如Notepad、VS Code编辑bat往往可以通过“插入Unicode控制字符”或直接粘贴\x1b的转义表示来插入。但超老实的Windows记事本不管显示与否复制粘贴都不会丢这个字符实测下来最稳。3.4 256色与RGB真彩色cmd也能玩出花ANSI转义序列不止16色这一档。现代终端基本都支持256色调色板和24位RGB真彩色。cmd在Windows 10较新版本中也兼容这两套规范。256色的前景色格式是echo ESC[38;5;编号m颜色文字ESC[0m编号范围0到255其中0到15就是上面提到的16种标准色16到231是6×6×6的彩色立方体232到255是灰度梯度。背景色则把38改成48。RGB真彩色格式是echo ESC[38;2;红;绿;蓝m颜色文字ESC[0m红、绿、蓝三个通道数值范围0到255。比如想要一个接近橙色的文本可以设置38;2;255;165;0。这一下就把cmd终端的色彩上限拉到了和现代编辑器同一个水平。不过提醒一句256色和RGB真彩色效果跟cmd窗口使用的字体和渲染方式有关老旧的“点阵字体”对色彩支持较差建议把窗口字体换成“Consolas”或者“新宋体”渲染效果会好很多。4. 批处理里的实战封装4.1 做一个返回值式的颜色输出函数在批处理里面如果要反复输出不同颜色的文字最讨厌的就是每个echo后面都要跟一长串转义序列。我习惯把颜色输出包装成一个函数放到脚本开头后面调用就清爽多了。echo off chcp 65001 nul call :EnableAnsi call :Cout 这是一条正常信息 0 call :Cout 这是一条成功提示 A call :Cout 这是一条警告信息 E call :Cout 这是一条错误信息 C pause exit /b :EnableAnsi for /f tokens2 delims. %%i in (ver) do set winver%%i reg query HKCU\CONSOLE /v VirtualTerminalLevel nul 2nul if errorlevel 1 ( reg add HKCU\CONSOLE /v VirtualTerminalLevel /t REG_DWORD /d 1 /f nul ) exit /b :Cout set msg%~1 set color%~2 echo %ESC%[3%color%m%msg%%ESC%[0m exit /b这个封装里%ESC%是一个提前定义好的环境变量存放ESC字符。实际运行前需要先用前面讲的办法把ESC字符插入到变量定义行。有了这个函数后面的所有输出都变成一行调用既清爽又能统一管理颜色。4.2 日志分级错误、警告、正常一眼看清运维场景里脚本执行时会产生大量日志最常见的是把不同级别的内容染不同颜色。我一般在批处理工具里做两层处理窗口输出实时染色同时把不带颜色的原始文本写入日志文件。核心思路是定义一个log函数它接受级别和消息两个参数。错误用亮红色警告用黄色成功用绿色普通信息用默认白色。代码可以这样写set LOG_FILE%~dp0run.log call :Log INFO 开始执行备份任务 call :Log OK 数据库备份完成 call :Log WARN 磁盘剩余空间不足 call :Log ERROR 网络连接超时重试失败 :Log set level%~1 set msg%~2 for /f tokens1-2 delims %%a in (%date% %time%) do set timestamp%%a %%b if /i %level%ERROR ( echo %ESC%[91m[%timestamp%] [ERROR] %msg%%ESC%[0m ) else if /i %level%WARN ( echo %ESC%[93m[%timestamp%] [WARN] %msg%%ESC%[0m ) else if /i %level%OK ( echo %ESC%[92m[%timestamp%] [OK] %msg%%ESC%[0m ) else ( echo [%timestamp%] [INFO] %msg% ) echo [%timestamp%] [%level%] %msg% %LOG_FILE% exit /b这个函数的好处是日志文件里是干净纯文本任何编辑器都能打开cmd窗口里则是彩色的分级信息一眼就能看到错误在哪一行。注意往日志文件写内容时一定要写未染色的原始文本。你要是把转义序列也写进文件以后用type或者记事本查看时文件里会莫名多出一堆[31m之类的字符很影响后续分析。4.3 交互式菜单给选项加上视觉引导交互菜单是cmd工具最容易让人犯迷糊的地方。选项一多用户不知道该看哪里。用ANSI转义序列把当前选项高亮出来或者把所有选项按不同色块区分交互体验会好很多。我之前写过一个批量部署工具菜单长这样echo %ESC%[36m%ESC%[0m echo %ESC%[33m 请选择要执行的操作%ESC%[0m echo %ESC%[32m [1] 启动服务%ESC%[0m echo %ESC%[32m [2] 停止服务%ESC%[0m echo %ESC%[33m [3] 重启服务%ESC%[0m echo %ESC%[31m [4] 退出%ESC%[0m echo %ESC%[36m%ESC%[0m用户反馈说有了颜色之后一眼就能分辨操作区和警示区不容易误选。这个小改动成本极低但对工具专业度的影响很大。4.4 动态进度条用颜色区分状态批处理里显示进度常见做法是重复echo加退格符配合颜色就能做出彩色状态条。虽然cmd不支持像Python那样灵活的光标控制但通过set /p配合\r回车符也能实现同一行刷新效果。下面是一个简化版每当文件拷贝完成就输出一行绿色“OK”出现失败时输出红色“FAIL”。配合延时看起来就像实时刷新一样。操作的时候留意别在关键业务逻辑里加太多ping 127.0.0.1 -n 1 nul这种延时否则拖慢整体速度。5. 在其它语言中给cmd输出上色5.1 Python脚本输出彩色文本很多人的工作流是用Python写工具然后在cmd窗口里运行。默认情况下Python的print函数不识别颜色转义序列你直接print(\033[31m红色\033[0m)在部分环境里会原样输出乱码。但实际上Windows 10之后的Python在启动时会自动调用控制台API让转义序列生效。前提是你用print输出的字符串确实包含真正的ESC字符而不是字面上的\033。最省心的办法是安装colorama库它底层会自动处理Windows和Unix的差异pip install colorama然后写from colorama import init, Fore, Back, Style init() print(f{Fore.RED}错误{Style.RESET_ALL}连接超时) print(f{Fore.GREEN}成功{Style.RESET_ALL}备份完成) print(f{Fore.YELLOW}{Back.BLUE}警告{Style.RESET_ALL}磁盘空间不足)init()会检测当前环境如果是cmd窗口就启用ANSI支持如果是重定向或管道环境可以传convertTrue参数colorama会把颜色标记剥离避免日志混乱。这段代码在cmd里运行输出就是标准的红绿黄彩色文本。如果不打算装第三方库也可以直接用系统APIimport os os.system() # 空命令仅让Windows启用ANSI支持 print(\033[31m红色\033[0m)实测下来os.system()这行会在某些Windows环境中生效但在精简版或老系统上不稳定没有colorama来得可靠。5.2 C/C程序控制cmd颜色C/C程序要控制cmd颜色除了输出ANSI转义序列还有一条更经典的路Windows控制台API函数叫SetConsoleTextAttribute。它直接修改当前控制台的文本属性不需要依赖终端是否支持ANSI兼容性更好。#include windows.h #include iostream int main() { HANDLE hOut GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_INTENSITY); std::cout 这是亮红色文字\n; SetConsoleTextAttribute(hOut, FOREGROUND_GREEN | FOREGROUND_BLUE); std::cout 这是青色文字\n; SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); std::cout 恢复默认颜色\n; return 0; }SetConsoleTextAttribute的第一个参数是控制台标准输出句柄第二个参数是颜色属性。颜色属性低四位是前景色高四位是背景色可以用|把多个FOREGROUND_*常量组合起来。这个函数的特点是不需要关心终端对ANSI的支持从XP到Windows 11都好使。C/C项目里如果既有跨平台需求又不想写两套代码我建议把ANSI转义序列作为主方案在Windows启动时调用一次SetConsoleMode开启虚拟终端处理。这样代码里可以统一用转义序列平台差异由一个初始化函数解决。这种写法在VS Code的终端里效果尤其好。6. 常见问题与排查技巧实录6.1 颜色不生效出来一串乱码最典型的现象是echo输出了类似[31m红色[0m的字符但颜色没变。原因基本可以锁定为ESC字符没有正确插入终端没有收到真正的转义序列把它当成了普通文本。排查步骤我建议按顺序来先用十六进制查看bat文件的ESC位置字符如果看到的是1B说明插入正确看到的是45 53 43那就是三个字母插入方式错了。再检查系统版本Windows 10以下基本不支持ANSI序列建议用color命令或者C/C的API。检查注册表HKCU\CONSOLE\VirtualTerminalLevel是否为1不是就改成1再重启cmd。最后排除第三方终端软件干扰Windows Terminal、ConEmu、Cmder对ANSI支持情况各有不同但通常都支持实在不行就在原生cmd窗口里试。6.2 重定向、管道、日志记录时颜色丢失把彩色输出重定向到文件文件里要么什么都没有要么出现一堆乱码。这个问题的根因是ANSI转义序列只在终端显示时有意义写入文件时它只是普通的字节序列。color命令重定向到文件后没有内容转义序列重定向到文件后则会产生[31m这类杂质。解决方案很简单不要让日志文件和终端输出共用同一个输出函数。作为个人脚本可以在颜色函数里写两行一行输出彩色到屏幕一行写纯文本到文件。前面我给的log函数就是这种模式。如果你在写Python脚本还可以配合logging模块的StreamHandler在控制台用彩色Formatter同时让FileHandler使用纯文本Formatter两者互不干扰。6.3 中文输出变乱码和颜色标记混在一起cmd默认代码页是GBK936如果你在批处理里用了UTF-8编码中文会显示成乱码如果你在脚本里执行了chcp 65001切换成UTF-8那输出里如果混入非UTF-8字符又会乱。这跟颜色本身没关系但颜色序列夹在中文中间时字符边界一乱整行就废了。我的经验是bat文件尽量用系统默认的GBK编码保存或者在文件开头统一执行一次chcp 65001 nul同时保证bat文件本身以UTF-8保存。二者必须匹配不然必出乱码。推荐用VS Code或者Notepad保存时能明确看到当前编码格式。6.4 修改用户名或拖拽文件到cmd无效顺带说两句有网友问为什么改了Windows用户名后cmd窗口路径仍显示旧用户名。这是因为cmd窗口的默认%USERPROFILE%变量和实际Shell路径是在进程启动时初始化的修改系统用户名不会自动更新当前运行的cmd实例重启终端就好了。还有一种情况是你改了用户目录的文件夹名但没有同步修改注册表里的ProfileImagePath这个需要专门处理不在颜色讨论范围内。另外Windows 11某个版本里拖拽文件到cmd窗口会出现“目标路径无效”之类的提示。这个问题本质上跟颜色输出无关大概率是系统更新后默认终端改成了Windows Terminalcmd的拖拽处理逻辑有变化。解决办法是把Windows Terminal的defaultProfile改成cmd或直接在Windows Terminal的标签页里拖文件。6.5 读写权限和脚本执行策略如果你在C盘系统目录下操作cmd偶尔会遇到写注册表或创建临时文件失败的情况。这跟颜色输出没直接关系但会让脚本在调用reg add开启ANSI支持时卡住。解决办法是避开C盘根目录和C:\Windows\System32把批处理和日志文件统一放在用户目录或D盘工作目录如果必须改注册表就用管理员权限运行cmd或者提前用icacls调整目录权限。我自己经常把脚本放到D:\scripts这种独立工作区颜色函数、日志输出、临时文件都围绕这个目录组织省了很多权限问题。7. 顺着这条路还能看那些相关用法把颜色玩熟之后很多之前觉得“没必要”的cmd功能都会顺带打开。比如用chcp切换代码页时配合颜色区分编码状态用ping测试端口时把成功失败输出染成不同颜色甚至把扫盘命令的结果按剩余空间量级着色这些都可以在一个脚本里统一起来。cmd最大的特点就是轻量、随叫随到配合ANSI序列以后它就不再是那个“又黑又土”的裸终端了。我个人在实际操作中的体会是颜色输出这事千万不要贪多。脚本里所有文字都花花绿绿反而失去了重点。把错误印成红色把成功印成绿色把警告印成黄色这三条用足就已经能覆盖90%的场景。再加一个“重置所有样式”的兜底习惯任何外部程序调用后都把颜色恢复回来你的cmd脚本就足够专业和稳了。最后再分享一个小技巧给常用脚本起一个统一的彩色启动横幅把脚本名称、版本号、作者用青色打印出来不仅好看别人拿到你的工具时也会觉得你考虑得很周到。