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

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

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

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

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

Responder1

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

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

Responder2

Existe alguma variável de ambiente?

Sim. É a TERMvariável de ambiente. Isso ocorre porque há 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. Na verdade, GNU grep, mencionado na resposta de M. Kitt, é um bom exemplo de 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 ser capaz de procurar o registro do tipo de terminal no banco de dados termcap/terminfo.
  • Então, portanto, deveserum 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, a localização do banco de dados termcap pode ser especificada usando uma TERMCAPvariável de ambiente. Portanto, em algumas implementações há umsegundovariável de ambiente.
  • O registro termcap/terminfo deve indicar que o tipo de terminal suporta cores. Há um max_colorscampo em terminfo. Não está definido para tipos de terminais que não possuem recursos de cores. Na verdade, existe uma convenção terminfo de que para cada tipo de terminal colorido há outro registro -mou -monoanexado ao nome que indica que não há capacidade de cores.
  • O registro termcap/terminfo deve fornecer a maneira para o programa mudar de cor. Existem campos set_a_foregrounde set_a_backgroundem terminfo.

É um pouco mais complexo do que apenas verificar isatty(). É feitoavançarcomplicado por várias coisas:

  • Algunsaplicativos adicionam opções de linha de comando ou sinalizadores de configuração que substituem a isatty()verificação, para que o programasempreoununcaassume que tem um terminal (colorido) como saída. Por exemplo:
    • GNU lstem a --coloropção de linha de comando.
    • BSD lsolha para CLICOLOR(sua ausência significanunca) e CLICOLOR_FORCE(sua presença significasempre) variáveis ​​de ambiente e também possui a -Gopção de linha de comando.
  • Algunsos aplicativos não usam termcap/terminfo e possuem respostas conectadas ao valor de TERM.
  • Nem todos os terminais usam sequências ECMA-48 ou ISO 8613-6 SGR, que são ligeiramente erroneamente chamadas de "sequências de escape ANSI", para mudança de cores. O mecanismo termcap/terminfo é, na verdade, projetado para isolar as aplicações do conhecimento direto das sequências de controle exatas. (Além disso, há um argumento de queninguémusa sequências ISO 8613-6 SGR, porquetodos concordam com o bugde usar ponto e vírgula como delimitador para sequências SGR de cores RGB. O padrão na verdade especifica dois pontos.)

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

OA porta Linux/Unix tem este código, que permite a colorização somente quando a TERMvariável de ambiente existe e seu valor não corresponde ao nome conectado dumb:

interno
deveria_colorizar (vazio)
{
  char const *t = getenv("TERMO");
  return t && strcmp (t, "burro") != 0;
}

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

OA porta Win32 tem este código, que permite a colorização quando a TERMvariável de ambientenãoexiste ou quando existe e seu valor não corresponde ao nome conectado dumb:

interno
deveria_colorizar (vazio)
{
  char const *t = getenv("TERMO");
  retornar ! (t && strcmp (t, "burro") == 0);
}

Os problemas do GNU grepcom cores

grepA colorização do GNU é realmente notória. Como na verdade ele não faz um trabalho adequado de construção da saída do terminal, mas apenas culpa algumas sequê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 ele exibe uma saída incorreta em certas circunstâncias.

É nessas circunstâncias que é necessário colorir algo que está na margem direita do terminal. Os programas que realizam a saída do terminal corretamente precisam levar em conta as margens direitas automáticas. Além dissoà pequena possibilidade de que o terminal possa não tê-los (ou seja, o auto_right_margincampo em terminfo), o comportamento dos terminais que possuem margens direitas automáticas geralmente segue o precedente DEC VT dequebra de linha pendente. O GNU grepnão leva em conta isso, esperando ingenuamentequebra de linha imediata, e sua saída colorida dá errado.

A saída colorida não é uma coisa simples.

Leitura adicional

Responder3

O unbuffercomando doesperarO pacote desacopla a saída do primeiro programa e a entrada do segundo programa.

Você usaria assim:

unbuffer myshellscript.sh | grep value

Eu uso isso o tempo todo com ansible e um homebrewedctescript para que eu possa ver a saída colorida no terminal, deixando o arquivo de log com saída normal (não colorida).

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

informação relacionada