¿Cómo decide un programa si tiene o no salida en color?

17

Cuando ejecuto un comando desde un terminal que imprime resultados en color (como lso gcc), se imprime el resultado en color. Según tengo entendido, el proceso en realidad está generando códigos de escape ANSI , y el terminal formatea el color.

Sin embargo, si ejecuto el mismo comando mediante otro proceso (digamos una aplicación C personalizada) y redirijo la salida a la propia salida de la aplicación, estos colores no persisten.

¿Cómo decide un programa si generar o no texto con formato de color? ¿Hay alguna variable de entorno?

Chris Smith
fuente

Respuestas:

25

La mayoría de estos programas solo envían códigos de color a un terminal por defecto; comprueban para ver si su salida es un TTY, usando isatty(3). Por lo general, hay opciones para anular este comportamiento: deshabilitar colores en todos los casos o habilitar colores en todos los casos. Para GNU, greppor ejemplo, --color=neverdeshabilita los colores y los --color=alwayshabilita.

En un shell, puede realizar la misma prueba con el -t testoperador: [ -t 1 ]solo tendrá éxito si la salida estándar es un terminal.

Stephen Kitt
fuente
¿Hay alguna forma de engañar a la aplicación iniciada para que el proceso sea un tty?
Chris Smith
44
Preguntado y respondido en unix.stackexchange.com/questions/249723 ya, chris13523. Los comentarios no son realmente el lugar para preguntas de seguimiento, por cierto.
JdeBP
1
@ chris13524 ver el enlace de JdeBP; También puede forzar a los programas a generar códigos de color en muchos casos (consulte mi respuesta actualizada).
Stephen Kitt
13

¿Hay alguna variable de entorno?

Si. Es la TERMvariable de entorno. Esto se debe a que hay varias cosas que se usan como parte del proceso de decisión.

Es difícil generalizar aquí, porque no todos los programas están de acuerdo en un solo diagrama de flujo de decisión. De hecho grep, GNU , mencionado en la respuesta de M. Kitt, es un buen ejemplo de un caso atípico que utiliza un proceso de decisión algo inusual con resultados inesperados. En términos muy generales, por lo tanto:

  • La salida estándar debe ser un dispositivo terminal, según lo determinado por isatty().
  • El programa debe poder buscar el registro para el tipo de terminal en la base de datos termcap / terminfo.
  • Por lo tanto, debe haber un tipo de terminal para buscar. La TERMvariable de entorno debe existir y su valor debe coincidir con un registro de base de datos.
  • Por lo tanto, debe haber una base de datos terminfo / termcap. En algunas implementaciones del subsistema, la ubicación de la base de datos termcap se puede especificar utilizando una TERMCAPvariable de entorno. Entonces, en algunas implementaciones hay una segunda variable de entorno.
  • El registro termcap / terminfo debe indicar que el tipo de terminal admite colores. Hay un max_colorscampo en terminfo. No está configurado para tipos de terminales que en realidad no tienen capacidades de color. De hecho, existe una convención terminfo de que para cada tipo de terminal que se puede colorear hay otro registro con el nombre -mo que se -monoagrega al mismo que no indica capacidad de color.
  • El registro termcap / terminfo debe proporcionar el camino para que el programa cambie los colores. Hay set_a_foregroundy set_a_backgroundcampos en terminfo.

Es un poco más complejo que solo verificar isatty(). Se complica aún más por varias cosas:

  • Algunas aplicaciones agregan opciones de línea de comandos o indicadores de configuración que anulan la isatty()verificación, de modo que el programa siempre o nunca asume que tiene un terminal (colorable) como salida. Por ejemplo:
    • GNU lstiene la --coloropción de línea de comandos.
    • BSD lsanaliza las variables de entorno CLICOLOR(su ausencia significa que nunca ) y CLICOLOR_FORCE(su presencia significa siempre ), y también presenta la -Gopción de línea de comandos.
  • Algunas aplicaciones no usan termcap / terminfo y tienen respuestas cableadas al valor de TERM.
  • No todos los terminales usan secuencias ECMA-48 o ISO 8613-6 SGR, que se denominan "secuencias de escape ANSI", para cambiar los colores. El mecanismo termcap / terminfo está diseñado para aislar las aplicaciones del conocimiento directo de las secuencias de control exactas. (Además, hay un argumento para argumentar que nadie usa las secuencias SGR ISO 8613-6, porque todos están de acuerdo con el error de usar punto y coma como delimitador para las secuencias SGR de color RGB. El estándar en realidad especifica dos puntos).

Como se mencionó, GNU en greprealidad exhibe algunas de estas complejidades adicionales. No consulta termcap / terminfo, cablea las secuencias de control para emitir y cablea una respuesta a la TERMvariable de entorno.

El puerto Linux / Unix tiene este código , que permite la coloración solo cuando TERMexiste la variable de entorno y su valor no coincide con el nombre cableado dumb:

En t
debe_colorizar (vacío)
{
  char const * t = getenv ("TÉRMINO");
  return t && strcmp (t, "tonto")! = 0;
}

Entonces, incluso si TERMes así xterm-mono, GNU grepdecidirá emitir colores, aunque otros programas como vimno lo hagan.

El puerto Win32 tiene este código , que permite la coloración cuando la TERMvariable de entorno no existe o cuando existe y su valor no coincide con el nombre cableado dumb:

En t
debe_colorizar (vacío)
{
  char const * t = getenv ("TÉRMINO");
  regreso ! (t && strcmp (t, "tonto") == 0);
}

grepLos problemas de GNU con el color

grepLa coloración de GNU es realmente notoria. Debido a que en realidad no hace un trabajo adecuado al construir la salida del terminal, sino que solo culpa a algunas secuencias de control cableadas en varios puntos de su salida con la vana esperanza de que eso sea lo suficientemente bueno, en realidad muestra una salida incorrecta en ciertas circunstancias.

Estas circunstancias son donde tiene que colorear algo que está en el margen derecho de la terminal. Los programas que realizan la salida del terminal correctamente deben tener en cuenta los márgenes derechos automáticos. Además de la ligera posibilidad de que el terminal no los tenga (es decir, el auto_right_margincampo en terminfo), el comportamiento de los terminales que tienen márgenes derechos automáticos a menudo sigue el precedente DEC VT de la línea pendiente pendiente . GNU grepno tiene en cuenta esto, ingenuamente espera un ajuste de línea inmediato , y su salida de color sale mal.

La salida en color no es una cosa simple.

Otras lecturas

JdeBP
fuente
2
Según tengo entendido, el OP pregunta por el cambio de comportamiento cuando se redirige la salida; $TERMNo explica eso. (Su respuesta es interesante en general, pero no creo que aborde la pregunta ...)
Stephen Kitt
muy interesante. He estado esperando una visión general como esta sobre cómo los programas descubren (o simplemente "deciden") cuáles son las capacidades de un terminal desde hace unos meses. Esto también da una idea de por qué es tan difícil encontrar una visión general como esta, porque cada programa parece hacerlo de manera ligeramente diferente.
the_velour_fog
Una explicación de lo que significa el ajuste de línea pendiente y el ajuste de línea inmediato junto con un ejemplo que demuestre la diferencia sería bueno.
mosvy
0

El unbuffercomando del paquete de espera desacopla la salida del primer programa y la entrada al segundo programa.

Lo usarías así:

unbuffer myshellscript.sh | grep value

Lo uso todo el tiempo con ansible y un script ctee casero para poder ver la salida de color en el terminal, mientras dejo el archivo de registro con salida normal (sin color).

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