Como um programa decide se deve ou não ter saída colorida?

17

Quando executo um comando de um terminal que imprime saída colorida (como lsou gcc), a saída colorida é impressa. Pelo que entendi, o processo está realmente emitindo códigos de escape ANSI e o terminal formata a cor.

No entanto, se eu executar o mesmo comando por outro processo (por exemplo, um aplicativo C personalizado) e redirecionar a saída para a própria saída do aplicativo, essas cores não persistirão.

Como um programa decide se deve ou não produzir texto com formato de cor? Existe alguma variável de ambiente?

Chris Smith
fonte

Respostas:

25

A maioria desses programas apenas envia códigos de cores para um terminal por padrão; eles verificam se a saída é um TTY usando isatty(3). Geralmente, existem opções para substituir esse comportamento: desativar cores em todos os casos ou ativar cores em todos os casos. Para o GNU, greppor exemplo, --color=neverdesativa as cores e as --color=alwaysativa.

Em um shell, você pode executar o mesmo teste usando o -t testoperador: [ -t 1 ]terá sucesso apenas se a saída padrão for um terminal.

Stephen Kitt
fonte
Existe alguma maneira de enganar o aplicativo iniciado que o processo é um tty?
Chris Smith
4
Já perguntado e respondido em unix.stackexchange.com/questions/249723 , chris13523. Os comentários não são realmente o lugar para perguntas subsequentes, a propósito.
JdeBP
1
@ chris13524 veja o link do JdeBP; você também pode forçar os programas a imprimir códigos de cores em muitos casos (consulte minha resposta atualizada).
Stephen Kitt
13

Existe alguma variável de ambiente?

Sim. É a TERMvariável de ambiente. Isso ocorre porque existem várias coisas que são usadas como parte do processo de decisão.

É difícil generalizar aqui, porque nem todos os programas concordam com um único fluxograma de decisão. De fato grep, o GNU , mencionado na resposta de M. Kitt, é um bom exemplo de um outlier que usa um processo de decisão um tanto incomum, com resultados inesperados. Em termos muito gerais, portanto:

  • A saída padrão deve ser um dispositivo terminal, conforme determinado por isatty().
  • O programa deve poder procurar o registro para o tipo de terminal no banco de dados termcap / terminfo.
  • Portanto, deve haver um tipo de terminal para procurar. A TERMvariável de ambiente deve existir e seu valor deve corresponder a um registro do banco de dados.
  • Portanto, deve haver um banco de dados terminfo / termcap. Em algumas implementações do subsistema, o local do banco de dados termcap pode ser especificado usando uma TERMCAPvariável de ambiente. Portanto, em algumas implementações, há uma segunda variável de ambiente.
  • O registro termcap / terminfo deve indicar que o tipo de terminal suporta cores. Há um max_colorscampo no terminfo. Não está definido para tipos de terminais que realmente não possuem recursos de cores. De fato, existe uma convenção terminfo de que para cada tipo de terminal colorido existe outro registro com -mou -monoanexado ao nome que não indica capacidade de cores.
  • O registro termcap / terminfo deve fornecer o caminho para o programa alterar as cores. Existem set_a_foregrounde set_a_backgroundcampos no terminfo.

É um pouco mais complexo do que apenas checar isatty(). É complicado ainda mais por várias coisas:

  • Alguns aplicativos adicionam opções de linha de comando ou sinalizadores de configuração que substituem a isatty()verificação, para que o programa sempre ou nunca assuma que ele possui um terminal (colorido) como saída. Por exemplo:
    • O GNU lstem a --coloropção de linha de comando.
    • O BSD lsanalisa as variáveis ​​de ambiente CLICOLOR(sua ausência significa nunca ) e CLICOLOR_FORCE(sua presença significa sempre ) e também exibe a -Gopção de linha de comando.
  • Alguns aplicativos não usam termcap / terminfo e têm respostas conectadas ao valor de TERM.
  • Nem todos os terminais usam sequências ECMA-48 ou ISO 8613-6 SGR, que são levemente denominadas "sequências de escape ANSI", para alterar as cores. O mecanismo termcap / terminfo é de fato projetado para isolar aplicativos do conhecimento direto das seqüências exatas de controle. (Além disso, existe um argumento de que ninguém usa sequências SGR ISO 8613-6, porque todos concordam com o erro de usar ponto e vírgula como delimitador para sequências SGR de cores RGB. O padrão realmente especifica dois pontos.)

Como mencionado, o GNU greprealmente exibe algumas dessas complexidades adicionais. Ele não consulta termcap / terminfo, conecta as seqüências de controle a serem emitidas e conecta uma resposta à TERMvariável de ambiente.

A porta Linux / Unix possui esse código , que permite a colorização somente quando a TERMvariável de ambiente existe e seu valor não corresponde ao nome do hardware dumb:

int
should_colorize (vazio)
{
  char const * t = getenv ("TERM");
  retornar t && strcmp (t, "burro")! = 0;
}

Portanto, mesmo que você TERMseja xterm-mono, o GNU grepdecidirá emitir cores, mesmo que outros programas como esse vimnão o façam.

A porta Win32 contém esse código , que permite a coloração quando a TERMvariável de ambiente não existe ou quando existe e seu valor não corresponde ao nome do hardware dumb:

int
should_colorize (vazio)
{
  char const * t = getenv ("TERM");
  Retorna ! (t && strcmp (t, "burro") == 0);
}

grepProblemas do GNU com cores

A coloração grepdo GNU é realmente notória. Como na verdade ele não faz um bom trabalho na construção da saída do terminal, mas apenas culpa algumas seqüências de controle conectadas em vários pontos de sua saída, na vã esperança de que isso seja bom o suficiente, na verdade exibe uma saída incorreta em determinadas circunstâncias.

É nessas circunstâncias que é necessário colorir algo que está na margem direita do terminal. Os programas que executam corretamente a saída do terminal devem contabilizar as margens direitas automáticas. Além da pequena possibilidade de o terminal não os ter (por exemplo, o auto_right_margincampo no terminfo), o comportamento dos terminais que possuem margens direitas automáticas geralmente segue o precedente DEC VT de quebra de linha pendente . O GNU grepnão responde por isso, ingenuamente esperando quebra automática de linha , e sua saída colorida dá errado.

Saída colorida não é uma coisa simples.

Leitura adicional

JdeBP
fonte
2
Pelo que entendi, o OP está perguntando sobre a mudança de comportamento quando a saída é redirecionada; $TERMnão explica isso. (A sua resposta é interessante em geral, mas eu não acho que aborda a questão ...)
Stephen Kitt
muito interessante. Eu estava querendo uma visão geral como esta sobre como os programas descobrem (ou simplesmente "decidem") quais são os recursos dos terminais há alguns meses. Isso também dá uma idéia de por que é tão difícil encontrar uma visão geral como essa - porque cada programa parece fazê-lo de maneira um pouco diferente.
the_velour_fog
Uma explicação sobre o significado de quebra de linha pendente e quebra de linha imediata , juntamente com um exemplo que demonstra a diferença, seria bom.
mosvy 10/07
0

O unbuffercomando do pacote expectável desacopla a saída do primeiro programa e a entrada para o segundo programa.

Você usaria assim:

unbuffer myshellscript.sh | grep value

Eu usá-lo o tempo todo com ansible e uma Homebrewed ETEc roteiro para que eu possa ver a saída de cores no terminal, deixando o arquivo de log com saída normal (não-colorizado).

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