How to check if Bash can print colors

40,148

Solution 1

The idea is for my application to know not to color the output if the program can't print, say, logging output from through a cron job to a file, no need to log colored output, but when running manually, i like to view the output colored

What language are you writing your application in?

The normal approach is to check if the output device is a tty, and if it is, check if that type of terminal supports colors.

In bash, that would look like

# check if stdout is a terminal...
if test -t 1; then

    # see if it supports colors...
    ncolors=$(tput colors)

    if test -n "$ncolors" && test $ncolors -ge 8; then
        bold="$(tput bold)"
        underline="$(tput smul)"
        standout="$(tput smso)"
        normal="$(tput sgr0)"
        black="$(tput setaf 0)"
        red="$(tput setaf 1)"
        green="$(tput setaf 2)"
        yellow="$(tput setaf 3)"
        blue="$(tput setaf 4)"
        magenta="$(tput setaf 5)"
        cyan="$(tput setaf 6)"
        white="$(tput setaf 7)"
    fi
fi

echo "${red}error${normal}"
echo "${green}success${normal}"

echo "${green}0.052${normal} ${bold}${green}2,816.00 kb${normal}"
# etc.

In C, you have to do a lot more typing, but can achieve the same result using isatty and the functions listed in man 3 terminfo.

Solution 2

This should be enough:

$ tput colors

tput colors explained:

If you look at the manpage, you'ill notice this:

SYNOPSIS
       tput [-Ttype] capname [parms ... ]

And...

   capname
          indicates the capability from the terminfo database.  When term‐
          cap  support is compiled in, the termcap name for the capability
          is also accepted.

The termcap colors is in the terminfo database, so you can ask for it. If you have a zero exit status, then the termcap is compiled in. But if you have somethin like:

$ tput unknowntermcap
tput: unknown terminfo capability 'unknowntermcap'
$ echo $?
4

This shows that unknowntermcap doesn't exist. So, this:

$ tput colors
8
$ echo $?
0

Shows that your command was right.

Other useful ways:

  • In C, you can just use isatty and see if it's a TTY
  • See if it's a dumb terminal looking $TERM variable

Cheers

Solution 3

The idea is for my application to know not to color the output if the program can't print, say, logging output from through a cron job to a file, no need to log colored output, but when running manually, i like to view the output colored.

For this use case, what programs typically do (e.g. GNU ls or GNU grep with --color=auto) is to use colors if their output is going to a terminal, and no colors otherwise. Terminals that don't support ANSI color-changing sequences are rare enough that it's acceptable to make their users override the default choice. In any case, make sure your application has an option to force colors on or off.

In a shell script, use [ -t 1 ] to test if standard output is a terminal.

# option processing has set $color to yes, no or auto
if [ $color = auto ]; then
  if [ -t 1 ]; then color=yes; else color=no; fi
fi

From a program using the C API, call isatty(1).

# option processing has set use_color to 0 for no, 1 for yes or 2 for auto
if (use_color == 2) use_color = isatty(1);

Solution 4

Running commands like less and looking at the output from a program that outputs using colors, the output is displayed wrong, like

[ESC[0;32m0.052ESC[0m ESC[1;32m2,816.00 kbESC[0m]

Try using less --RAW-CONTROL-CHARS.

In this example, I am using logtool, which prints output using colors.

Without --RAW-CONTROL-CHARS:

$ head -20 /var/log/messages | logtool | less
ESC[0mESC[0;37mMar 20 11:43:52ESC[0mESC[1;36m host1ESC[0mESC[0;37m rsyslogd:ESC[0m ^GESC[0;31mlast message repeated 14 timesESC[0mESC[0m

With --RAW-CONTROL-CHAR:

$ head -20 /var/log/messages | logtool | less --RAW-CONTROL-CHARS
Mar 20 11:43:52 host1 rsyslogd: ^Glast message repeated 14 times

Imagine this is in pretty colors. Also, I am not sure why that ^G is being displayed.

Solution 5

That would be the fault of less not being set to interpret ANSI escapes; look for R in $LESSOPTS. As for determining if the system knows your terminal can deal with colors, tput colors will output either the number of colors it supports or -1 if it doesn't support colors. (Note that some terminals may use xterm instead of xterm-color as their terminal description, but still support colors.)

Share:
40,148

Related videos on Youtube

Angelo Vargas
Author by

Angelo Vargas

Updated on September 17, 2022

Comments

  • Angelo Vargas
    Angelo Vargas over 1 year

    I want to know if there's any way to check if my program can output terminal output using colors or not.

    Running commands like less and looking at the output from a program that outputs using colors, the output is displayed incorrectly, like this:

    [ESC[0;32m0.052ESC[0m ESC[1;32m2,816.00 kbESC[0m]

  • Angelo Vargas
    Angelo Vargas about 13 years
    The idea is for my application to know not to color the output if the program can't print, say, logging output from through a cron job to a file, no need to log colored output, but when running manually, i like to view the output colored.
  • l0b0
    l0b0 about 13 years
    colors is not documented in the tput man page (!), so should I look for a number >= 8 in stdout or a return code of 0?
  • D4RIO
    D4RIO about 13 years
    Seemed obvious, but your comment shows that it isn't. I'm adding that info (briefly, colors is a capability of terminfo database)
  • Mikel
    Mikel about 13 years
    The colors capability is documented in terminfo(5). Testing using tput -T dumb colors, tput -T vt220 colors, tput -T linux colors, tput -T xterm colors suggests common values are -1 (no color support) and 8 (8 colors). Note that this only applies after checking the output device is a terminal (e.g. [ -t 1 ] or isatty).
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 12 years
    Note that tput colors returns what the local terminal database thinks of the terminal. This may or may not correspond to what the terminal can actually do, especially for a terminal type like xterm which comes in many variants (ranging from black and white to 256 colors).
  • srdubya
    srdubya about 4 years
    I found that each $(tput setaf <#>)" needed to be wrapped in \[ \] to work properly. For example: white="\[$(tput setaf 7)\]. Without this, I experienced weird history editing behavior in vi mode in BASH 5.0.16(1)-release.
  • Mikel
    Mikel about 4 years
    @srdubya Yes, if you're doing that in your bash prompt.
  • JdeBP
    JdeBP almost 4 years
    At the time of writing this question, and some years afterward, relying upon the exact value of the capability was problematic, because a 16-bit compatibility requirement in the data file format prevented it from correctly reporting 24-bit colour. Since 2018, a new file format can handle 32-bit numeric terminfo capabilities. stackoverflow.com/a/36163656/340790
  • Thomas Guyot-Sionnest
    Thomas Guyot-Sionnest over 3 years
    AFAIK you don't even need the 2nd check, tput colors, tput should only print escape codes if supported by the terminal.
  • Konrad Rudolph
    Konrad Rudolph over 2 years
    Unfortunately there seem to be specific situations where test -t 1 fails even though colours are supported. To give a concrete example, I have a “post-install” script in an RPM package (generated by fpm). When running the script via yum install my.rpm (interactively), test -t 1 returns 1 even though colours are correctly displayed. Any idea why that might be?
  • Konrad Rudolph
    Konrad Rudolph over 2 years
    (I should note that the identical script correctly detects colours when generating a DEP or APK package via fpm, the issue only manifests on CentOS with RPM packages, so I’m fairly confident that this isn’t simply an error in the script.)
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 2 years
    @KonradRudolph test -t 1 checks if the output is a terminal. It's impossible to fully reliably know whether the output is going to a place that supports color changing escape sequences. For example, some terminals don't support colors (termcap/terminfo can help here, but it isn't 100% reliable either). In the other direction, it's impossible to distinguish my_code | less (doesn't support colors) from my_code | less -R (does support colors), or to know how the log file will be viewed in my_code >log.txt.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 2 years
    @KonradRudolph I guess the yum installation is piping the output, maybe | tee log.txt or some such. If so then presumably either you're not supposed to use colored output, or you're supposed to force colors on (--color=always rather than --color=auto) if you want to have colors.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 2 years
    @KonradRudolph The value of shell has no influence on whether the process run by subprocess.run and friends has a terminal on stdout/stderr. Only whether the Python process itself has a terminal on stdout/stderr, and obviously redirection options.
  • Konrad Rudolph
    Konrad Rudolph over 2 years
    Yes, never mind, that was nonsense (made a mistake).