1. printf 打印红色字体:终端日志高亮到底难在哪
printf打印红色字体,本质是往标准输出里塞一段 ANSI 转义序列(ANSI escape sequence),让终端把后续字符渲染成红色,再用重置序列把颜色关掉。它不是什么特殊 API,也不需要引入第三方库,一行printf("\x1b[31m%s\x1b[0m\n", msg)就能跑。适合谁?写命令行工具的人、维护 Shell 部署脚本的人、在 C/C++ 里打日志的人,以及天天盯 CI 输出、希望错误行一眼跳出来的运维同学。
但真正落地时会发现坑不少:\e和\x1b到底用哪个、31和91有什么区别、重定向到文件后为什么多出一堆^[[31m乱码、Windows 的 cmd 为什么不认、日志被tee之后颜色去哪了。这些问题不解决,红色字体要么不生效,要么污染日志文件。
我试过在一个批量部署脚本里直接写死\033[31m,本地终端漂亮得很,结果脚本被 Jenkins 调用、输出重定向到build.log之后,日志里全是转义字符,排查问题时反而更难读。后来才把「是否着色」做成一个开关,根据isatty和NO_COLOR环境变量动态决定。
这篇就按「能直接复制、能直接验证」的思路来:先给最小可用的红色输出片段,再讲清 ANSI 转义序列的结构和颜色码对照,然后分别给 Shell、C、Python 三种常见场景的可复制配置,接着用命令验证颜色是否真的生效、不同终端怎么检查兼容性,最后把 401、乱码、颜色不重置这些真实报错逐个拆开。全程围绕printf、ANSI转义序列、字体颜色三个关键词展开,你照着敲就能看到红色。
需要说明的是,本文所有示例都在本地终端和常见 Linux 发行版自带的 bash 下验证过,不涉及任何网络访问配置。如果你在团队里需要统一管理脚本里用到的模型调用凭据,可以顺带了解下 TaoToken 的 API Key 管理方式,后文会给出具体入口,但它和终端着色本身是两件独立的事,不影响你先把颜色跑通。
2. TaoToken 前置:把着色脚本和凭据管理分开
先说清楚定位,避免误解。printf打印红色字体是纯终端行为,不依赖任何外部服务,你完全可以在断网机器上跑通。之所以在这一节提 TaoToken,是因为很多命令行工具和脚本在打日志的同时,还会调用模型接口做摘要、做错误归类,这时候脚本里就会出现「既要管颜色输出、又要管 API Key」的情况。把这两件事分开管理,脚本才干净。
TaoToken 在这里扮演的是「统一凭据入口」的角色:你在它的控制台里创建 API Key,脚本通过环境变量读取,而不是把 Key 硬编码进带颜色输出的日志函数里。这样即使日志被重定向、被采集,也不会把凭据一起打出去。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数,配置时别把查询串一起粘进去。
具体操作路径:登录后进入控制台,找到 API Keys 页面创建一把 Key,复制出来先存到本地密码管理器。然后在脚本里这样读取:
export TAOTOKEN_API_KEY="你的Key"注意不要写成echo $TAOTOKEN_API_KEY去打日志,哪怕加了红色也不行,颜色不解决泄露问题。正确的做法是日志里只打印「Key 已加载」这类状态,比如:
if [ -n "$TAOTOKEN_API_KEY" ]; then printf "\x1b[32m[OK]\x1b[0m API Key loaded\n" else printf "\x1b[31m[ERR]\x1b[0m API Key missing\n" fi这里绿色表示正常、红色表示缺失,正好把本文的颜色能力用上。你可以把这段当成脚本启动自检的一部分。
关于模型 ID 和 Base URL 的对应关系,如果你后续要在脚本里调用对话接口,需要三件套齐全:Base URL 填https://taotoken.net/api,Key 用上面创建的,Model ID 按控制台文档里列出的可用模型填写。三者缺一,请求就会失败,常见表现是 401 或返回体里读不到choices,这两种错误在第五节会详细拆。
需要提醒的是,TaoToken 是凭据与接口管理平台,不是编辑器插件,也不替代你本地的终端工具。你仍然是在自己的 bash、zsh、cmd 里跑printf,颜色渲染由终端负责,和平台无关。把边界划清楚,后面配置才不会互相干扰。
如果你暂时不需要调用任何接口,这一节可以只做一件事:把TAOTOKEN_API_KEY环境变量准备好,然后继续往下看颜色配置。颜色部分不依赖它,先跑通颜色,再回头接接口,顺序更稳。
3. 可复制配置:Shell、C、Python 三套红色输出片段
这一节给可直接复制的片段,路径和写法都按常见环境来。核心结构永远是三段:进入颜色、输出内容、重置颜色。少最后一段,后面所有输出都会变红,这是最常见的翻车点。
先看 ANSI 转义序列的基本结构:ESC[开头,中间是参数,最后是字母m表示「设置图形属性」。ESC的写法有几种等价形式,\x1b、\033、\e在多数 Shell 和 C 编译器里都能用,但\e不是标准 C,跨编译器时建议统一用\x1b或\033。红色前景色的码是31,亮红是91,重置是0。
Shell 场景,最稳的写法是用printf而不是echo,因为echo对转义的处理在不同 Shell 下不一致:
#!/usr/bin/env bash RED='\x1b[31m' BRIGHT_RED='\x1b[91m' RESET='\x1b[0m' printf "${RED}%s${RESET}\n" "这是一行红色错误日志" printf "${BRIGHT_RED}%s${RESET}\n" "这是亮红色,适合强调"注意变量里存的是字面量\x1b,printf的格式串会解释它。如果你用echo -e,在 dash 下可能不生效,所以统一printf更省心。
C 场景,用宏定义把颜色码集中管理,这也是很多嵌入式日志库的做法:
#include <stdio.h> #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_YELLOW "\x1b[33m" #define ANSI_COLOR_RESET "\x1b[0m" int main(void) { printf("%sget emb part info from nvtpack node is failed: %s %u%s\n", ANSI_COLOR_RED, "part_name", 42u, ANSI_COLOR_RESET); return 0; }编译gcc -o demo demo.c后运行,失败信息就是红色。这里把重置放在行尾,保证只影响这一行。
Python 场景,用\033更直观:
RED = "\033[31m" RESET = "\033[0m" def log_error(msg: str) -> None: print(f"{RED}{msg}{RESET}") log_error("连接超时,请检查网络配置")如果你希望「重定向到文件时自动去掉颜色」,加一个判断:
import sys def colorize(text: str, code: str = "31") -> str: if not sys.stdout.isatty(): return text return f"\033[{code}m{text}\033[0m"isatty()为假说明输出被管道或文件接管,此时返回纯文本,日志文件就干净了。这个判断在 Shell 里对应[ -t 1 ],在 C 里对应isatty(STDOUT_FILENO)。
颜色码对照表,方便你按需扩展:
| 前景色 | 普通码 | 亮色码 | 典型用途 |
|---|---|---|---|
| 黑 | 30 | 90 | 少用 |
| 红 | 31 | 91 | 错误、失败 |
| 绿 | 32 | 92 | 成功、通过 |
| 黄 | 33 | 93 | 警告 |
| 蓝 | 34 | 94 | 信息 |
| 品红 | 35 | 95 | 调试 |
| 青 | 36 | 96 | 提示 |
| 白 | 37 | 97 | 普通文本 |
背景色是40到47,亮背景100到107。组合写法用分号,比如红底白字是\x1b[41;37m。但日志场景建议只用前景色,背景色在深色终端下容易糊成一片。
还有一个实用技巧:加粗用1,下划线用4,反显用7。比如\x1b[1;31m是加粗红,比亮红更醒目,兼容性也更好。重置时统一\x1b[0m就能把所有属性清掉,不用逐个关闭。
4. 验证请求与成功结果:用命令确认颜色真的生效
配置写完不能只看代码,得用命令验证。最直接的办法是把转义序列喂给printf,看终端是否渲染成红色。
第一步,最小验证:
printf '\x1b[31mRED TEST\x1b[0m\n'如果终端显示红色的RED TEST,说明终端支持 ANSI。如果显示的是\x1b[31mRED TEST\x1b[0m原文,说明当前环境不解析转义,常见于某些 Windows 终端或把输出重定向到了文件。
第二步,验证变量写法:
RED='\x1b[31m'; RESET='\x1b[0m' printf "${RED}%s${RESET}\n" "变量方式红色"这一步确认你的引号没写错。单引号里\x1b保持字面量,交给printf解释;如果误用双引号且 Shell 是 bash,\x可能被提前处理,导致输出异常。
第三步,验证重定向行为:
printf '\x1b[31mFILE TEST\x1b[0m\n' > /tmp/color_test.txt cat -v /tmp/color_test.txtcat -v会把不可见字符显示成^[[31m这种形式。如果你看到^[[31mFILE TEST^[[0m,说明转义序列确实写进了文件,只是文件不渲染颜色。这正是日志文件出现乱码的原因,解决办法就是前面说的isatty判断。
第四步,验证tput方案作为备选:
tput setaf 1; printf "tput red\n"; tput sgr0tput会读取当前终端的 terminfo 数据库,兼容性比硬编码转义更好,但依赖TERM环境变量正确。如果echo $TERM是dumb,tput会失败,此时回到硬编码方案。
第五步,检查终端类型:
echo $TERM常见值xterm-256color、screen-256color、linux都支持基本 ANSI。如果是dumb或空值,颜色大概率不生效,需要先设置export TERM=xterm-256color。
成功结果应该同时满足三条:终端里看到红色文本、cat -v能看到^[[31m、重定向到文件后文件里是纯文本(如果你加了isatty判断)。三条都过,说明配置既能在交互终端着色,又不会污染日志。
如果你在脚本里同时调用了模型接口,验证顺序建议是:先跑颜色验证,确认终端没问题;再用curl单独测接口连通性;最后把两者合到一个脚本里。分开验证的好处是出错时能快速定位是颜色问题还是接口问题。
5. 本篇常见错排查:401、乱码、颜色不重置逐个拆
真实报错就那么几类,逐个对照。
报错一:401 Unauthorized。这个和颜色无关,出现在你脚本里调用接口时。原因通常是 Key 没加载、Key 复制时带了空格、或者 Base URL 写错。检查顺序:先echo ${TAOTOKEN_API_KEY:+set}确认变量非空(注意别直接 echo 值),再确认请求地址是https://taotoken.net/api而不是带 UTM 的官网地址。三件套 Base URL、Key、Model ID 缺一不可,Model ID 写错有时也表现为 401 或 404,按控制台文档核对。
报错二:日志里出现^[[31m或\x1b[31m原文。这是转义序列没被解释。分两种情况:一是输出被重定向到文件或管道,终端不参与渲染,属于正常现象,用isatty判断规避;二是当前终端不支持 ANSI,比如老版本 Windows cmd,需要开启虚拟终端支持,或改用支持 ANSI 的终端程序。检查命令:printf '\x1b[31mX\x1b[0m\n' | cat -v,看到^[[31m说明序列写对了,只是消费端不渲染。
报错三:颜色不重置,后面所有输出都变红。九成是漏了\x1b[0m,或者重置放在了错误的位置。比如printf "\x1b[31m%s\n" "$msg"没有重置,后续输出全红。正确写法是把重置拼在格式串末尾:printf "\x1b[31m%s\x1b[0m\n" "$msg"。另一个隐蔽原因是%s里本身含有换行,导致重置跑到下一行开头,视觉上像没重置,实际是顺序问题。
报错四:local proxy failed或连接类错误。这类错误出现在接口调用环节,和终端着色无关。先确认本机网络能正常访问目标地址,再检查脚本里是否误设了HTTP_PROXY之类的环境变量。排查时用env | grep -i proxy看一眼,有残留就unset。注意本文不涉及任何网络访问配置指导,只做环境变量残留检查。
报错五:reading choices相关错误。通常是接口返回体不是预期 JSON,脚本里解析choices字段时失败。先用curl把原始返回打出来看结构,确认返回的是错误对象还是正常响应。如果是错误对象,回到 401 那条排查凭据;如果是正常响应但字段名不同,按实际返回调整解析路径。调试时可以把原始返回用红色打出来,正好用上本文的颜色能力:
printf "\x1b[31m%s\x1b[0m\n" "$RAW_RESPONSE"报错六:OAuth 相关提示。如果你用的是需要 OAuth 流程的工具,出现授权失败时先检查回调地址和本地端口是否被占用,再确认凭据是否过期。这类问题和printf颜色无关,单独排查,别混在一起改,否则容易把本来正常的颜色配置改坏。
排查通用原则:颜色问题和接口问题分开验证。颜色用printf单行测试,接口用curl单独测试,两者都通过再合并。合并后出错,先注释掉接口调用只留颜色,逐步加回,定位最快。
6. 继续深入:把颜色能力沉淀成可复用函数
颜色配置跑通之后,建议别停在散落的printf上,把它收成一个函数或一个小头文件,团队里复用。Shell 里可以这样封装:
log_err() { printf "\x1b[31m[ERR] %s\x1b[0m\n" "$*"; } log_warn() { printf "\x1b[33m[WARN] %s\x1b[0m\n" "$*"; } log_ok() { printf "\x1b[32m[OK] %s\x1b[0m\n" "$*"; }调用时log_err "部署失败"即可,重置逻辑内置,不会漏。C 里对应把宏和fprintf(stderr, ...)结合,错误走 stderr、普通信息走 stdout,重定向时能分开采集。
再进一步,把「是否着色」做成全局开关,支持NO_COLOR环境变量(这是社区约定,设置后应禁用颜色)和FORCE_COLOR(强制着色,适合 CI 里想要彩色日志的场景)。判断顺序建议:FORCE_COLOR优先,其次看NO_COLOR,最后看isatty。这样本地交互有颜色、重定向自动降级、CI 里可强制开启,三种场景都覆盖。
如果你后续要在脚本里接入模型能力做日志摘要,凭据统一从 TaoToken 控制台管理,入口在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。需要在线验证模型返回时用模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,长期跑编码类任务可以看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。这些入口和终端颜色互不干扰,按需取用即可。
最后留一个实操建议:把本文的验证命令存成一个color_check.sh,换机器、换终端、升级系统后先跑一遍,三十秒确认颜色链路没断。终端渲染这种事,环境一变就可能出问题,有个自检脚本比事后翻日志省事得多。