
Когда я выполняю команду с терминала, который печатает цветной вывод (например, 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
опцию командной строки.
- В GNU
- Некоторыйприложения не используют 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
не учитывает это, наивно ожидаянемедленный перенос строки, и его цветной вывод становится неправильным.
Цветная печать — непростая вещь.
дальнейшее чтение
- Томас Э. Дики (2016).
grep --color
не показывает правильный вывод".часто задаваемые вопросы по xterm. Невидимый остров. - Джонатан де Бойн Поллард (2016).Курсив и цвет на страницах руководства на виртуальном терминале пользовательского пространства Nosh. Пакет «Перекус».
решение3
Команда unbuffer
отожидатьпакет разделяет вывод первой программы и ввод второй программы.
Вы можете использовать это так:
unbuffer myshellscript.sh | grep value
Я использую его все время с ansible и самодельнымceeскрипт, чтобы я мог видеть цветной вывод на терминале, оставляя при этом файл журнала с обычным (неокрашенным) выводом.
unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log