其实这是个我很久以前就遇到的问题,但是对tmux功能基本上 没有影响,所以一直忍啊忍,现在我终于忍不下去了……

tmux只有8种颜色?

就像这样:

好吧这不是世界末日,但是也够悲剧了:现在是21世纪,我 的显示器能显示2^32种颜色,可是我眼前的竟然是这种东西?!

这破事发生在我的linode主机上,而且只发生在我用putty登录 的时候;在gnome-terminal下用ssh直接连的话tmux能以256色 模式运行,tput colors返回256,vim看起来也没那么可怜:

外事不决问google

Google一下tmux+color,发现到处都有人在问“怎么我的tmux颜 颜色不对”,答案也不尽相同,但是基本都是这两种说法:

  1. tmux认为你在用的终端模拟程序弱到不支持256色,可 以通过tmux -2强制其进入256色模式,或者在.tmux.conf 里加入set -g default-terminal "screen-256color"

  2. 远程主机上的shell进程认为你的终端模拟程序弱到不支持 256色,所以没有设对相应的环境变量,这样除了tmux,其 他程序颜色也会不正常;在.bashrc或者.bash_profile 里面加上export TERM=xterm-256color就可以了。

试了一下,发现解释1里的两种设置方法都没用——vim的颜色还 是不对——但是两种设置方法都用上的话,vim的颜色竟然就坑爹 地对了……

解释2里的方法则是效果拔群;很显然tmux和vim的颜色模式会 受TERM环境变量影响,只有在TERM变量声明终端支持256色的时 候,tmux和vim才会工作在256色模式下。

终端模拟程序、tmux、命令行程序的行为模式

所以,TERM这个环境变量可以理解为终端导出的一个接口,供 shell和其他在终端下执行的命令行程序查询终端的类型,从 而适配不同的终端。

tmux这种特殊的程序,本身需要作为终端,处理命令行程序 的输入输出,同时又要做为命令行程序,在真正的终端下执行; 如果终端是插座,命令行程序是插头,那tmux就是排插……或者 如果你想装一下,叫它中间件也行。

所以,tmux一方面会去读终端提供的TERM变量,另一方面又会 给它体内的命令行程序提供TERM变量。在没做任何设置的情况 下,tmux外TERM的值是xterm,而tmux内的值则是screen.

上面效果拔群的方法2其实是让shell无视所有终端(包括tmux) 的TERM设置,直接将终端类型声明强制为支持256色——想象一 下你的业务逻辑代码越过操作系统抽象层直接call了系统调 用stime把系统时间改了,有够恶心的对吧……如果真的有个 终端是不支持256色的,其他命令行程序也不可能知道了。

对tmux和终端运用软件工程提供的方法

好吧因为我在装,所以我认为方法2可行但是不正确。正确的 做法是,尊重别人给你的接口,不要越过系统边界。问 题的症结其实是,我在用的所有终端(包括tmux)都支持256色, 但是却都谎称自己不支持,所以只要通过设置让它们自己别再 说谎就可以了。

我这用的终端模拟器是putty,"Connection" -> "Data" -> "Terminal-type string" 这里可以设置对外宣称的终端类型,默认是xterm,改为 xterm-256color;tmux则是在.tmux.confi里加上 set -g default-terminal "screen-256color",让tmux宣称 自己是支持256色的screen. 这下看起来(各种意义上)舒服 多了吧……

不过这样搞还有个悲剧的问题:tmux中的TERM变量永远是 screen-256color,不会根据外面终端支持的颜色数 改变——虽然tmux本身还是会在8色和256色模式下自动转换。 也就是说,tmux的工作模式和它宣称自己是哪个终端类型没 有关系,在8色模式下工作时可以宣称自己是256色的,反之 在256色模式下工作时也可以宣称自己是8色的……

为什么tmux要将这两个东西分开?大概tmux的作者也是出于 无奈,因为“终端类型”包含的信息太多了,除了支持的颜色数, 还有对各种转义串的兼容性等也靠“终端类型”来标识;我上面 将TERM变量称为“接口”,而这个接口红果果地违反了“只做一 件事情”的指导原则,导致副作用太多,结果就是不能随便改。