Как программа решает, следует ли выводить цветные данные?

Как программа решает, следует ли выводить цветные данные?

Когда я выполняю команду с терминала, который печатает цветной вывод (например, lsили gcc), печатается цветной вывод. Насколько я понимаю, процесс на самом деле выводитEscape-коды ANSI, и терминал форматирует цвет.

Однако если я выполню ту же команду другим процессом (например, пользовательским приложением на языке C) и перенаправлю вывод на собственный вывод приложения, эти цвета не сохранятся.

Как программа решает, выводить ли текст в цветовом формате? Есть ли какая-то переменная окружения?

решение1

Большинство таких программ по умолчанию выводят на терминал только цветовые коды; они проверяют, является ли их вывод TTY, используяisatty(3). Обычно есть варианты переопределить это поведение: отключить цвета во всех случаях или включить цвета во всех случаях. grepНапример, для GNU --color=neverотключает цвета и --color=alwaysвключает их.

В оболочке вы можете выполнить тот же тест, используя-t testоператор: [ -t 1 ]будет успешным только в том случае, если стандартный вывод является терминалом.

решение2

Есть ли какая-то переменная окружения?

Да. Это TERMпеременная окружения. Это потому, что есть несколько вещей, которые используются как часть процесса принятия решения.

Здесь трудно обобщать, потому что не все программы согласны с единой схемой принятия решений. Фактически, GNU grep, упомянутый в ответе М. Китта, является хорошим примером исключения, которое использует несколько необычный процесс принятия решений с неожиданными результатами. В очень общих чертах, поэтому:

  • Стандартный вывод должен быть терминальным устройством, как определено isatty().
  • Программа должна иметь возможность искать запись для типа терминала в базе данных termcap/terminfo.
  • Поэтому, следовательно, должно бытьбытьтип терминала для поиска. TERMПеременная среды должна существовать, а ее значение должно соответствовать записи базы данных.
  • Поэтому должна быть база данных terminfo/termcap. В некоторых реализациях подсистемы местоположение базы данных termcap может быть указано с помощью TERMCAPпеременной окружения. Поэтому в некоторых реализациях естьвторойпеременная окружения.
  • Запись termcap/terminfo должна указывать, что тип терминала поддерживает цвета. В terminfo есть поле max_colors. Оно не установлено для типов терминалов, которые на самом деле не имеют цветовых возможностей. Действительно, существует соглашение terminfo, что для каждого окрашиваемого типа терминала есть другая запись с именем -mили добавленная к нему, которая указывает на отсутствие цветовых возможностей.-mono
  • Запись termcap/terminfo должна предоставлять программе возможность менять цвета. В terminfo есть поля set_a_foregroundи .set_a_background

Это немного сложнее, чем просто проверка isatty(). Это сделанодальшеосложняется несколькими вещами:

  • Некоторыйприложения добавляют параметры командной строки или флаги конфигурации, которые отменяют isatty()проверку, так что программавсегдаилиникогдапредполагает, что у него есть (раскрашиваемый) терминал в качестве выхода. Например:
    • В GNU lsесть --colorопция командной строки.
    • BSD lsсмотрит на CLICOLOR(его отсутствие означаетникогда) и CLICOLOR_FORCE(его присутствие означаетвсегда) переменные среды, а также поддерживает -Gопцию командной строки.
  • Некоторыйприложения не используют termcap/terminfo и имеют жестко заданные ответы на значение TERM.
  • Не все терминалы используют последовательности ECMA-48 или ISO 8613-6 SGR, которые немного неправильно называются "последовательностями ANSI escape", для изменения цветов. Механизм termcap/terminfo на самом деле разработан для изоляции приложений от прямого знания точных последовательностей управления. (Более того, есть аргумент, чтониктоиспользует последовательности SGR ISO 8613-6, потому чтовсе согласны с ошибкой(Использования точки с запятой в качестве разделителя для последовательностей цветов RGB SGR. Стандарт фактически определяет двоеточие.)

Как уже упоминалось, GNU grepна самом деле демонстрирует некоторые из этих дополнительных сложностей. Он не консультируется с termcap/terminfo, жестко запрограммирует управляющие последовательности для эмиссии и жестко запрограммирует ответ на TERMпеременную окружения.

TheПорт Linux/Unix содержит этот код, который включает раскрашивание только в том случае, если TERMпеременная окружения существует и ее значение не совпадает с фиксированным именем dumb:

инт
should_colorize (недействительно)
{
  char const *t = getenv ("TERM");
  return t && strcmp (t, "dumb") != 0;
}

Поэтому даже если TERMу вас xterm-mono, GNU grepрешит испускать цвета, даже если другие программы, такие как , этого vimделать не будут.

TheПорт Win32 имеет этот код, что позволяет выполнять раскрашивание либо когда TERMпеременная окружениянесуществует или когда он существует и его значение не соответствует фиксированному имени dumb:

инт
should_colorize (недействительно)
{
  char const *t = getenv ("TERM");
  возврат ! (t && strcmp (t, "dumb") == 0);
}

grepПроблемы GNU с цветом

Раскрашивание GNU grepна самом деле печально известно. Поскольку оно на самом деле не выполняет надлежащую работу по созданию терминального вывода, а просто вставляет несколько жестко зашитых управляющих последовательностей в различных точках своего вывода в тщетной надежде, что этого достаточно, оно на самом деле отображает неправильный вывод в определенных обстоятельствах.

Эти обстоятельства — когда он должен раскрасить что-то, что находится на правом краю терминала. Программы, которые правильно выполняют вывод на терминал, должны учитывать автоматические правые поля. Кроме тогоиз-за небольшой вероятности того, что терминал может их не иметь (т. е. поле auto_right_marginв terminfo), поведение терминалов, которые имеют автоматические правые поля, часто следует прецеденту DEC VTожидание переноса строки. GNU grepне учитывает это, наивно ожидаянемедленный перенос строки, и его цветной вывод становится неправильным.

Цветная печать — непростая вещь.

дальнейшее чтение

решение3

Команда unbufferотожидатьпакет разделяет вывод первой программы и ввод второй программы.

Вы можете использовать это так:

unbuffer myshellscript.sh | grep value

Я использую его все время с ansible и самодельнымceeскрипт, чтобы я мог видеть цветной вывод на терминале, оставляя при этом файл журнала с обычным (неокрашенным) выводом.

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

Связанный контент