据我所知,一个行为良好的命令行程序应该检查$TERM
环境变量,获取终端仿真器的名称,在数据库中查找其条目terminfo
,然后使用这些转义码来控制终端设置,例如颜色、字体、样式、擦除屏幕以及设置光标位置。
然而,我是一个懒惰的程序员,如果我想让一些文本变成绿色,那么到目前为止最简单的事情就是直接在我的程序中嵌入 ANSI 转义码:
print("\033[32mhi\033[0m")
以这种方式更改终端颜色是简单的实现,使我不必学习新的依赖项(例如 ncurses),使我不必运行任何外部程序(例如tput
),并允许我用任何具有字符串的语言编写程序,而不是任何具有字符串的语言终端库。
我心中的理想主义者认为,依赖一个标准总是比依赖一个特定的实现更好。我的 macOS 安装附带了 2000 多个终端定义/usr/share/terminfo
。另一方面,实用主义者注意到多年来每个人都在嵌入 ANSI 代码,而且一切似乎都很好。
我经常看到避免使用辅助库而直接使用 ANSI 代码的代码,或者仅检查是否$TERM
为 以外的任何内容的软件,或者根本dumb
不检查的软件。$TERM
我很少看到有人抱怨这一点,或请求支持其他终端类型。
另外,我在网上看到了很多很多的帖子,没有提及任何其他终端类型或转义代码,将 ANSI 设置为“如何在终端中进行粗体和颜色”。看起来所有其他转义代码集确实已经消失,并且 ANSI 代码(或 ECMA-48 代码或 VT102 代码)已成为开发社区所确定的事实上的标准。
所以,我的问题是:如果我在 2019 年编写命令行软件,是否可以安全地假设它只会在使用 ANSI 的终端中使用?
编辑:人们建议tput
在运行时调用将颜色打印到屏幕上,而无需处理 terminfo。这是有效的,并且在许多情况下是一个很好的解决方案,但在我看来,它引入了不必要的运行时成本,因为它必须在我的程序启动时多次执行单独的二进制文件。
答案1
如果我正确理解这个问题,它真的可以归结为意见吗?下面尝试总结一些值得考虑的点
并非所有输出都到终端
尽管听起来微不足道,但即使世界上的每个终端都能正确理解 ANSI 序列(情况并非如评论中提到的那样,对于有关以下内容的陈述)全部找到一个反例就足以拒绝该语句):考虑重定向到文件或通过其他程序(例如,交互式使用grep
或脚本化的程序调用)输出的两种情况。
在这些情况下,通常希望能够获得如下输出:不是着色以简化处理。在这些情况下,完全依赖于环境TERM
变量仍然是不够的,例如,可以交互地输入管道,这将导致TERM
设置但颜色输出(可能)不合需要。
颜色可以不同
我已经编写了相当多的脚本,它们仅独立输出原始 ANSI 序列TERM
(通常带有打开/关闭着色的选项)。使用不同的终端仿真器、shell 甚至同一终端仿真器的选项,我经常收到不同颜色的输出。很少(但这种情况存在,并且都可以在现代 Linux 系统上找到)我还注意到颜色超出了识别范围(例如黑色上的深灰色等)。有时,一些本来应该是绿色的东西也被应用了“划掉”效果......
为了获得“有趣的洞察力”,请无条件地(即独立于 )进行任何使用大量 ANSI 转义序列的测试,TERM
然后在:rxvt
、gnome-terminal
、xterm
最后在 Linux 控制台上运行它。
如果不使用抽象层之一,尽管目标应用程序可以识别并处理转义序列,但着色总是有可能失败。
结论
在 2019 年,假设原始 ANSI 转义序列将按预期工作并不完全安全。
考虑一下终端控制对您的应用程序有多重要:如果它很重要(从某种意义上说,如果没有颜色/终端控制,它就会失去重要的功能),那么原始 ANSI 转义序列出现问题的风险就太高了。如果它不是那么重要(例如,养眼的日志、精美的交互式菜单与简单的交互式菜单等),那么采用非常有限的彩色输出抽象(例如原始 ANSI 序列+关闭所有颜色的选项)似乎是合理的)。
其他相对化陈述
当需要彩色输出或其他终端控制序列的同一应用程序也需要获取时输入以一种特殊的方式,那么是时候寻求一个彻底开发的解决方案,如 ncurses 或更“现代”的东西......
正如评论中所指出的:只有经过测试的环境对于任何软件来说都是真正安全的。如果您了解环境,则始终可以(尽管以一些抽象的“可移植性”为代价)向您的程序添加在该环境中实现的假设。
最后,程序是实际上,在所有情况下使用带有终端信息的更高级库并不安全:颜色随配置的终端配色方案而变化,并且某些效果(例如下划线)不适用于所有终端和字体组合。然后,在某些情况下,TERM
设置“不正确”会导致应用程序失败(我在运行tmux
over时经常遇到问题ssh
)。
答案2
我同意OP的观点。出于所有实际目的,非 ANSI 终端已经过时。如果您不相信我,请在 2021 年(现在)尝试使用 Wyse 或其他一些非 ANSI 终端。您很快就会发现,就像我一样,您需要使用“screen”或其他一些程序来制作您正在运行的程序“看到”ANSI 终端。
花在支持非 ANSI 终端上的努力同样是浪费,社区确实应该放弃这一特殊的技术债务。
话虽如此,有微妙的各个 ANSI 实现之间存在差异,您肯定需要在几个不同的终端仿真器上尝试您的程序,以确保您坚持使用受良好支持的命令。