
Cuando ejecuto un comando desde una terminal que imprime una salida en color (como ls
o gcc
), se imprime la salida en color. Según tengo entendido, el proceso en realidad está generandoCódigos de escape ANSI, y el terminal formatea el color.
Sin embargo, si ejecuto el mismo comando mediante otro proceso (por ejemplo, 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 genera o no texto con formato de color? ¿Existe alguna variable de entorno?
Respuesta1
La mayoría de estos programas sólo envían códigos de color a una terminal de forma predeterminada; verifican si su salida es un TTY, usandoisatty(3)
. Generalmente existen opciones para anular este comportamiento: deshabilitar colores en todos los casos o habilitar colores en todos los casos. Para GNU, grep
por ejemplo, --color=never
desactiva los colores y --color=always
los activa.
En un shell puedes realizar la misma prueba usando el-t
test
operador:[ -t 1 ]
tendrá éxito sólo si la salida estándar es un terminal.
Respuesta2
¿Existe alguna variable de entorno?
Sí. Es la TERM
variable de entorno. Esto se debe a que hay varias cosas que se utilizan como parte del proceso de decisión.
Es difícil generalizar aquí, porque no todos los programas coinciden en un único diagrama de flujo de decisión. De hecho GNUgrep
, mencionado en la respuesta de M. Kitt, es un buen ejemplo de un valor atípico que utiliza un proceso de decisión algo inusual con resultados inesperados. Por lo tanto, en términos muy generales:
- La salida estándar debe ser un dispositivo terminal, según lo determinado por
isatty()
. - El programa debe poder buscar el registro del tipo de terminal en la base de datos termcap/terminfo.
- Por lo tanto debe haberserun tipo de terminal para buscar. La
TERM
variable de entorno debe existir y su valor debe coincidir con un registro de la base de datos. - Por lo tanto, debe existir una base de datos terminfo/termcap. En algunas implementaciones del subsistema, la ubicación de la base de datos termcap se puede especificar usando un
TERMCAP
variable de entorno. Entonces, en algunas implementaciones hay unasegundoVariable ambiental. - El registro termcap/terminfo debe indicar que el tipo de terminal admite colores. Hay un
max_colors
campo en terminfo. No está configurado para tipos de terminales que en realidad no tienen capacidades de color. De hecho, existe una convención terminfo según la cual para cada tipo de terminal coloreable hay otro registro-m
adjunto-mono
al nombre que indica que no hay capacidad de color. - El registro termcap/terminfo debe proporcionar la forma para que el programa cambie de color. Hay campos
set_a_foreground
yset_a_background
en terminfo.
Es un poco más complejo que simplemente comprobarisatty()
. Está hechomáscomplicado por varias cosas:
- AlgunoLas aplicaciones agregan opciones de línea de comandos o indicadores de configuración que anulan el
isatty()
verificación, de modo que el programasiempreonuncaasume que tiene un terminal (colorable) como salida. Por ejemplo:- GNU
ls
tiene la--color
opción de línea de comandos. - BSD
ls
mira elCLICOLOR
(su significado de ausencia)nunca) yCLICOLOR_FORCE
(su presencia significasiempre) variables de entorno y también cuenta con la-G
opción de línea de comandos.
- GNU
- Algunolas aplicaciones no usan termcap/terminfo y tienen respuestas programadas al valor de
TERM
. - No todos los terminales utilizan secuencias ECMA-48 o ISO 8613-6 SGR, que reciben un ligero nombre incorrecto "secuencias de escape ANSI", para cambiar de color. De hecho, el mecanismo termcap/terminfo está diseñado para aislar las aplicaciones del conocimiento directo de las secuencias de control exactas. (Además, se puede argumentar quenadieutiliza secuencias ISO 8613-6 SGR, porquetodos están de acuerdo en el errorde utilizar punto y coma como delimitador para secuencias SGR de color RGB. El estándar en realidad especifica dos puntos).
Como se mencionó, GNU grep
en realidad presenta algunas de estas complejidades adicionales. No consulta termcap/terminfo, cablea las secuencias de control para emitir y cablea una respuesta a la TERM
variable de entorno.
ElEl puerto Linux/Unix tiene este código, que permite la coloración solo cuando la TERM
variable de entorno existe y su valor no coincide con el nombre cableadodumb
:
En t debería_colorizar (nulo) { char const *t = getenv ("TERMINO"); return t && strcmp (t, "tonto") != 0; }
Entonces, incluso si lo TERM
es xterm-mono
, GNU grep
decidirá emitir colores, aunque otros programas como vim
no lo harán.
ElEl puerto Win32 tiene este código, que permite la colorización ya sea cuando la TERM
variable de entornono esexiste o cuando existe y su valor no coincide con el nombre cableadodumb
:
En t debería_colorizar (nulo) { char const *t = getenv ("TERMINO"); devolver ! (t && strcmp (t, "tonto") == 0); }
Los problemas de GNU grep
con el color
grep
La 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 simplemente culpa a algunas secuencias de control cableadas en varios puntos de su salida con la vana esperanza de que sea lo suficientemente buena, en realidad muestra una salida incorrecta en ciertas circunstancias.
En estas circunstancias es donde hay que colorear algo que se encuentra en el margen derecho del terminal. Los programas que realizan salidas de terminal correctamente deben tener en cuenta los márgenes derechos automáticos. Ademása la mínima posibilidad de que el terminal no los tenga (a saber, elauto_right_margin
campo en terminfo), el comportamiento de los terminales que sí tienen márgenes derechos automáticos a menudo sigue el precedente DEC VT deajuste de línea pendiente. ÑUgrep
no tiene en cuenta esto, esperando ingenuamenteajuste de línea inmediatoy su salida en color sale mal.
La impresión en color no es algo sencillo.
Otras lecturas
- Thomas E. Dickey (2016). "
grep --color
no muestra la salida correcta".Preguntas frecuentes sobre xterm. Isla invisible. - Jonathan de Boyne Pollard (2016).Cursiva y color en páginas de manual en una terminal virtual de espacio de usuario. El paquete de comida.
Respuesta3
Elunbuffer
comando de laesperarEl paquete 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 casero.cteescript para poder ver la salida en color en la terminal, mientras dejo el archivo de registro con una salida normal (sin color).
unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log