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


17

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

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

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

Ответы:


25

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

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


Есть ли способ обмануть запущенное приложение, что процесс является tty?
Крис Смит

4
На вопрос и ответ на unix.stackexchange.com/questions/249723 уже, chris13523. Кстати, комментарии не являются местом для последующих вопросов.
JdeBP

1
@ chris13524 см. ссылку JdeBP; Вы также можете заставить программы выводить цветовые коды во многих случаях (см. мой обновленный ответ).
Стивен Китт

13

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

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

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

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

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

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

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

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

ИНТ
should_colorize (void)
{
  char const * t = getenv ("TERM");
  return t && strcmp (t, "dumb")! = 0;
}

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

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

ИНТ
should_colorize (void)
{
  char const * t = getenv ("TERM");
  возвращение ! (t && strcmp (t, "dumb") == 0);
}

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

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

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

Цветной вывод не простая вещь.

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


2
Насколько я понимаю, ОП спрашивает об изменении поведения при перенаправлении вывода; $TERMне объясняет это (Ваш ответ в целом интересен, но я не думаю, что он отвечает на вопрос ...)
Стивен Китт

очень интересно. Я хотел получить обзор, подобный этому, о том, как программы обнаруживают (или просто «решают»), каковы возможности терминала в течение нескольких месяцев. Это также дает представление о том, почему так сложно найти такой обзор - потому что каждая программа, кажется, делает это немного по-своему.
the_velour_fog

Было бы неплохо объяснить, что означают переносы ожидающей строки и немедленная перенос строки, а также пример, демонстрирующий разницу.
Мосвы

0

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

Вы бы использовали это так:

unbuffer myshellscript.sh | grep value

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

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.