重定向程序输出时,转义序列为何会被自动移除?
问题
很多带命令行界面的Linux程序会使用不同的转义序列为文本上色,例如通过0x1B[31m序列将文本设为红色。我编写的Pascal程序在输出重定向到文件时,会保留这些转义序列;但像git status这类命令,输出重定向到文件后仅包含纯文本,无转义序列。
例如我的Pascal程序:
program ColorPrinting; begin writeln(#27'[31m', 'Hello, world!', #27'[0m') end.
将其输出重定向后,文件中确实包含转义序列:
$ ./test | hexdump -C 00000000 1b 5b 33 31 6d 48 65 6c 6c 6f 2c 20 77 6f 72 6c |.[31mHello, worl| 00000010 64 21 1b 5b 30 6d 0a |d!.[0m.| 00000017
但对Git程序(已暂存内容显示为绿色,未暂存为红色)执行相同操作时,输出为纯文本:
$ git status -s > git-status.out && hexdump -C git-status.out 00000000 41 20 20 2e 67 69 74 69 67 6e 6f 72 65 0a 3f 3f |A .gitignore.??| 00000010 20 67 69 74 2d 73 74 61 74 75 73 2e 6f 75 74 0a | git-status.out.| 00000020 3f 3f 20 74 65 73 74 2e 6f 75 74 0a 3f 3f 20 74 |?? test.out.?? t| 00000030 65 73 74 2e 70 61 73 0a |est.pas.| 00000038
我原本以为程序本身并不知晓输出的去向,重定向由Bash Shell完成,那么这种差异是如何实现的?是Bash自动清理部分程序输出流中的转义序列,还是另有原因?
解答
这种差异和Bash没有关系,核心原因是程序自身会主动判断输出目标是否为终端,进而决定是否输出颜色转义序列。
在Linux系统中,程序可以通过isatty()系统调用检测标准输出(stdout)是否连接到终端设备:
- 如果检测到输出目标是终端,就输出带颜色的转义序列,让终端渲染出彩色文本;
- 如果输出被重定向到文件、管道或者其他非终端设备,就自动关闭颜色输出,只输出纯文本。
你的Pascal程序属于“硬编码”输出转义序列,不管输出到哪里都会直接写入这些序列,所以重定向到文件后依然会保留。而Git这类成熟的命令行工具做了终端检测逻辑:
- 默认情况下,只有当stdout是终端时才开启颜色输出;
- 若要强制Git输出颜色(哪怕重定向到文件),可以使用
git status -s --color=always参数,这样转义序列会被保留到输出文件中。
内容的提问来源于stack exchange,提问作者username
相关产品推荐
相关产品推荐

