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

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

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

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

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

答案1

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

在 shell 中,您可以使用以下命令执行相同的测试-t test运算符:[ -t 1 ]仅当标准输出是终端时才会成功。

答案2

是否有一些环境变量?

是的。它是TERM环境变量。这是因为有几件事被用作决策过程的一部分。

这里很难一概而论,因为并非所有程序都同意单一决策流程图。事实上,grepM. Kitt 的回答中提到的 GNU 是一个异常值的好例子,它使用了一些不寻常的决策过程,并产生了意想不到的结果。因此,一般来说:

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

这比仅仅检查要复杂一些isatty()。它被制成更远由几件事复杂化:

  • 一些应用程序添加命令行选项或配置标志来覆盖isatty()检查,以便程序总是或者绝不假设它有一个(可着色的)终端作为其输出。举些例子:
    • GNUls--color命令行选项。
    • BSDls着眼于CLICOLOR(它的缺席意味着绝不) 和CLICOLOR_FORCE(其存在意义总是)环境变量,并且还支持-G命令行选项。
  • 一些应用程序不使用 termcap/terminfo 并且对 的值有硬连线响应TERM
  • 并非所有终端都使用 ECMA-48 或 ISO 8613-6 SGR 序列来更改颜色,这些序列的名称稍有错误,即“ANSI 转义序列”。 termcap/terminfo 机制实际上旨在将应用程序与精确控制序列的直接知识隔离开来。 (此外,还有一种说法是没有人使用 ISO 8613-6 SGR 序列,因为每个人都同意这个错误使用分号作为 RGB 颜色 SGR 序列的分隔符。该标准实际上指定了冒号。)

如前所述,GNUgrep实际上表现出了一些额外的复杂性。它不咨询 termcap/terminfo,硬连线要发出的控制序列,以及硬连线对TERM环境变量的响应。

它的 Linux/Unix 端口有这个代码TERM,仅当环境变量存在且其值与硬连线名称不匹配时才启用着色dumb

整数
应该着色(空)
{
  char const *t = getenv("TERM");
  return t && strcmp(t, "哑巴") != 0;
}

因此,即使您的TERMxterm-mono,GNUgrep也会决定发出颜色,即使其他程序(例如)vim不会。

它的Win32端口有这个代码,这可以在以下情况下实现着色TERM环境变量才不是存在或者当它存在并且其值与硬连线名称不匹配时dumb

整数
应该着色(空)
{
  char const *t = getenv("TERM");
  返回 ! (t && strcmp(t, "哑巴") == 0);
}

GNUgrep的颜色问题

GNUgrep的彩色化实际上是臭名昭著的。因为它实际上并没有正确地构建终端输出,而只是在其输出中的各个点上指责一些硬连线控制序列,徒劳地希望这足够好,所以它实际上在某些情况下显示了不正确的输出。

在这些情况下,它必须对终端右侧边缘的某些内容进行着色。正确执行终端输出的程序必须考虑自动右边距。 此外终端可能没有它们(即auto_right_marginterminfo 中的字段)的可能性很小,具有自动右边距的终端的行为通常遵循 DEC VT 先例待处理换行。 GNUgrep没有考虑到这一点,天真地期待着立即换行,并且其彩色输出出错。

彩色输出并不是一件简单的事情。

进一步阅读

答案3

unbuffer命令来自预计包将第一个程序的输出与第二个程序的输入解耦。

你可以像这样使用它:

unbuffer myshellscript.sh | grep value

我一直将它与 ansible 和自制程序一起使用立方体脚本,以便我可以在终端上看到颜色输出,同时使日志文件保留正常(非彩色)输出。

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log

相关内容