程序如何确定是否有彩色输出?


17

从打印彩色输出的终端(例如lsgcc)执行命令时,将打印彩色输出。据我了解,该过程实际上是在输出ANSI转义码,并且终端会格式化颜色。

但是,如果我通过另一个进程(例如自定义C应用程序)执行相同的命令,并将输出重定向到应用程序自己的输出,则这些颜色不会持久。

程序如何确定是否以彩色格式输出文本?是否有一些环境变量?

Answers:


25

大多数此类程序默认情况下仅将颜色代码输出到终端。他们使用来检查其输出是否为TTY isatty(3)。通常,有一些选项可以替代此行为:在所有情况下都禁用颜色,或者在所有情况下都启用颜色。grep例如,对于GNU ,--color=never禁用颜色并--color=always启用它们。

在外壳程序中,您可以使用-t test运算符执行相同的测试:[ -t 1 ]仅当标准输出为终端时,该测试才会成功。


有什么办法欺骗启动的应用程序该进程是tty吗?
克里斯·史密斯

4
unix.stackexchange.com/questions/249723上已被询问和回答,chris13523。顺便说一句,评论并不是真正的后续问题。
JdeBP '16

1
@ chris13524参见JdeBP的链接;您还可以在许多情况下强制程序输出颜色代码(请参阅我更新的答案)。
史蒂芬·基特

13

是否有一些环境变量?

是。它是TERM环境变量。这是因为在决策过程中有几件事被使用。

在这里很难一概而论,因为并非所有程序都在一个决策流程图上达成一致。实际上GNUgrep,在M. Kitt的回答中提到的是一个离群值的好例子,该离群值使用了一些异常的决策过程并产生了意外的结果。因此,从一般意义上讲:

  • 标准输出必须是终端设备,具体取决于 isatty()
  • 该程序必须能够在termcap / terminfo数据库中查找终端类型的记录。
  • 因此,必须一个终端类型来查找。的TERM环境变量必须存在和它的价值必须与数据库记录相匹配。
  • 因此,必须有一个terminfo / termcap数据库。在子系统的某些实现中,可以使用TERMCAP环境变量指定termcap数据库的位置。因此,在一些实现有一个第二环境变量。
  • termcap / terminfo记录必须声明终端类型支持颜色。max_colorsterminfo中有一个字段。未为实际上不具有颜色功能的终端类型设置此功能。确实,有一个terminfo约定,对于每种可着色的终端类型,都存在另一条记录,该记录带有-m-mono附加了没有颜色功能的名称。
  • termcap / terminfo记录必须提供程序更改颜色的方式。terminfo中有set_a_foregroundset_a_background字段。

它比仅检查要复杂一些isatty()。有几件事使它变得更加复杂:

  • 某些应用程序添加了覆盖该isatty()检查的命令行选项或配置标志,因此该程序始终永远不会假定其具有(可着色的)终端作为其输出。举些例子:
    • GNU ls具有--color命令行选项。
    • BSD会ls查看环境变量CLICOLOR(其缺失表示Never)和CLICOLOR_FORCE(存在度始终表示)环境变量,并使用-G命令行选项。
  • 某些应用程序不使用termcap / terminfo,并且对的值具有硬连线的响应TERM
  • 并非所有终端都使用ECMA-48或ISO 8613-6 SGR序列来更改颜色,这些序列被误称为“ ANSI转义序列”。实际上,termcap / terminfo机制旨在将应用程序与直接的确切控制序列知识隔离开来。(此外,有人争论说没有人使用ISO 8613-6 SGR序列,因为每个人都同意使用分号作为RGB颜色SGR序列的定界符的错误。该标准实际上指定了冒号。)

如前所述,GNU grep实际上表现出其中一些额外的复杂性。它不参考termcap / terminfo,硬连接要发出的控制序列,硬连接对TERM环境变量的响应。

它的Linux / Unix端口具有以下代码该代码仅在TERM环境变量存在且其值与硬接线名称不匹配时才启用着色dumb

整型
should_colorize(无效)
{
  char const * t = getenv(“ TERM”);
  返回t && strcmp(t,“ dumb”)!= 0;
}

因此,即使您TERMxterm-mono,GNU grep也会决定发出颜色,即使其他程序vim也不会。

它的Win32端口具有以下代码,当TERM环境变量存在或环境变量存在且其值与硬接线名称不匹配时,可以启用着色dumb

整型
should_colorize(无效)
{
  char const * t = getenv(“ TERM”);
  退货!(t && strcmp(t,“ dumb”)== 0);
}

GNU grep的颜色问题

GNU grep的着色实际上是臭名昭著的。因为它实际上并没有正确地构建终端输出,而只是在输出的各个点责备几个硬接线控制序列,但徒劳地希望这足够好,所以在某些情况下它实际上显示了错误的输出。

在这些情况下,必须在终端的右边缘上涂一些颜色。正确执行终端输出的程序必须考虑自动右边距。 除了终端可能没有它们的可能性很小(请参阅auto_right_marginterminfo中的字段),具有自动右边界的终端的行为通常遵循挂起换行的DEC VT先例。GNU grep并没有考虑到这一点,天真地希望立即换行,并且其彩色输出出错。

彩色输出不是一件容易的事。

进一步阅读


2
据我了解,OP正在询问有关输出重定向时行为的变化。$TERM没有解释。(您的回答总体上很有趣,但我认为它不能解决问题...)
斯蒂芬·基特

很有意思。我一直想要这样的概述,几个月来程序是如何发现(或简单地“决定”)终端功能的。这也提供了一个见解,为什么如此难找到这样的概述-因为每个程序似乎做的都略有不同。
the_velour_fog's

解释一下挂起的换行立即换行的含义以及一个演示差异的示例将是不错的选择。
mosvy

0

Expect包中的unbuffer命令将第一个程序的输出与第二个程序的输入解耦。

您可以这样使用它:

unbuffer myshellscript.sh | grep value

我一直在使用ansible和自制的ctee脚本来使用它,因此我可以在终端上看到颜色输出,而将日志文件保留为正常(非颜色)输出。

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.