读取xterm中的当前文本颜色

35 投票
4 回答
35164 浏览
提问于 2025-04-16 07:55

我正在写一些小工具,发现彩色文本挺好用的。其实也没什么复杂的,就是用了一些转义序列。我创建了一个简单的类,里面有一个pprint(msg, color)的函数。找到了一些代码后,我很容易就让它工作起来了,具体的代码可以在这里找到。

我遇到的问题是,打印完彩色文本后,我应该能够把颜色关掉。比如说,用户运行我的程序,几乎所有的文本都是默认的终端颜色,但如果出现错误,我想把错误信息打印成红色。我在错误信息前面加上'\033[0;32m',结果信息就变成红色了。不幸的是,之后所有的文本都变成红色,直到我手动去改。这在我的程序运行时还好,因为我知道每条信息应该是什么颜色。但是,程序结束后颜色依然保持不变。基本上,我想在程序开始时读取当前的颜色,然后在结束时恢复它。就像脚本退出时恢复当前工作目录一样。

我该如何读取当前的转义序列呢?

系统环境: Red Hat 5.x Bash Python 2.3

谢谢你的帮助。

4 个回答

6

其实这是可能的——对于xterm和一些兼容的终端来说。

比如说,xtermcontrol使用了OSC 10这个控制序列来获取默认的前景色和背景色。从2002年起,这个功能就在xterm中被记录下来了。

对于其他终端来说:

  • 在RHEL 5中,"Terminal"程序是gnome-terminal 2.16.0;这个版本不支持OSC 10(在相应的CentOS 5中测试过)。
  • 这个问题是在2010年提出来的,提到的是Red Hat的企业版本,更新速度比Debian还慢。
  • 往后看,Debian 7上的gnome-terminal 3.4.1.1(2012年初)也不支持这个控制序列。
  • 最后,在Debian 8的3.14.1版本(2014年末)中,这个功能得到了支持。
  • CentOS 7的gnome-terminal 3.14.3也支持这个控制序列。

想知道这个功能是什么时候加上的,得注意VTE的开发者并不写文档。所以...查看git日志可以看到

commit 1b8c6b1aac587b79476a60a5830385abc939430d 
Author: Egmont Koblinger <egmont@gmail.com> 
Date:   Wed Jan 22 00:13:51 2014 +0100

    emulation: Add support for OSC 1?1[017] (fg, bg, highlight colors)

    https://bugzilla.gnome.org/show_bug.cgi?id=567444

另一方面,默认颜色和当前颜色是不一样的。用户从1999年起就可以通过xterm使用DECRQSS控制序列来做到这一点。也就是说,把终端设置为原始模式,然后做一些类似的操作

printf '\033P$m\033\\'

就能让它回复一个包含SGR参数的字符串。

如果颜色是通过SGR设置的,那么这些代码会包含在回复中,例如:

\033P1$r0;33m\033\\

表示前景色编号3(编码为33)。

你可以到此为止(因为你可以提取这些参数并在之后重新使用它们来设置终端为相同的状态),但如果想获取实际的RGB颜色,可以使用OSC 4。你需要使用颜色编号(来自SGR序列),然后发送类似这样的内容:

printf '\033]4;3;?\033\\'

所以在xterm中这肯定是可行的。在下一个xterm更新中会有一个关于DECRQSS的演示/测试脚本。

对于其他程序,你需要更多的时间:

  • xtermcontrol的开发者忽视了DECRQSS(它没有设置/获取SGR代码的功能)。

  • VTE的开发者在收到错误报告后会复制xterm的功能;VTE的源代码中没有提到DECRQSS。它的git日志在2009年提到过OSC 4,但实现不完整(只允许设置颜色,而不是获取颜色)。

162

与其使用复杂的转义序列,不如直接使用 tput 这个工具。下面是我在 ~/.bashrc 文件中用来设置我的提示符 PS1 的一段代码:

BLACK=$(tput setaf 0)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
LIME_YELLOW=$(tput setaf 190)
POWDER_BLUE=$(tput setaf 153)
BLUE=$(tput setaf 4)
MAGENTA=$(tput setaf 5)
CYAN=$(tput setaf 6)
WHITE=$(tput setaf 7)
BRIGHT=$(tput bold)
NORMAL=$(tput sgr0)
BLINK=$(tput blink)
REVERSE=$(tput smso)
UNDERLINE=$(tput smul)

如果你想把颜色重置回正常的终端颜色,可以在最后加上 ${NORMAL},像这样:

echo "${RED}这段是红色 ${NORMAL}这段是正常颜色"

4

我觉得这不太可能实现,而且如果能实现的话,可能也不太适合在不同的环境中使用。你能做的最好的事情就是发送 sgr0,这个命令会把所有的设置恢复到默认状态(而不是恢复到之前的状态)。在 xterm 终端中,sgr0 对应的命令是 Esc[m。如果你只想重置颜色,而不影响其他设置,可以发送 op,在 xterm 中这个命令是 Esc[39;49m

这些命令不应该直接写死在代码里。你应该使用 terminfo、termcap 或 [n]curses 这些工具。

撰写回答