24

In a shell script, how can I test programmatically whether or not the terminal supports 24-bit or true color?


Related: This question is about printing a 24-bit / truecolor test pattern for eyeball verification

3
  • 4
    Not a direct answer, but if you're OK with falling back to less granular colour, you can just print the 256-color escape code immediately before the 24-bit code (e.g. \e[48;5;124m\e[48;2;65;0;0m for a red background). Terminals that don't support 24-bit colour will ignore the second escape code and use the less-precise colour from the palette, while those that do will read it and immediately override the 256-color code. Commented Oct 29, 2019 at 21:53
  • 1
    @HarryCutts Nice one. Is there a way to map a 24-bit colour to it's closest 8-bit neighbour?
    – Tom Hale
    Commented Nov 3, 2019 at 11:55
  • I couldn't find one after a cursory search, so you might have to write your own. Also, I only tried it with shades of gray before, and now that I'm trying it with colours it seems that the 24-bit code is resetting the background to black, so it might be a bit of a niche solution. Commented Nov 5, 2019 at 23:39

3 Answers 3

19

This source says to check if $COLORTERM contains 24bit or truecolor.

sh

[ "$COLORTERM" = truecolor ] || [ "$COLORTERM" = 24bit ]

bash / zsh:

[[ $COLORTERM =~ ^(truecolor|24bit)$ ]]
4
  • 5
    This is not a reliable solution. What if the env variable is not set?
    – jdhao
    Commented Oct 16, 2019 at 5:14
  • @jdhao Some variables like $TERM are just assumed to be set correctly. If not set, then I'd fallback to checking for 256 colour support myself.
    – Tom Hale
    Commented Oct 16, 2019 at 6:07
  • 3
    One thing to note: This works well with local terminals, but when you get into a remote machine (e.g., ssh) or a tmux session, the COLORTERM variable would not propagate. Commented Mar 20, 2020 at 1:46
  • 4
    @JongwookChoi ssh -o SendEnv=MYVAR
    – Tom Hale
    Commented Apr 27, 2020 at 12:31
3

Just use tput colors. I believe testing for terminal capabilities is a much safer test than parsing $TERM or $COLORTERM yourself.

if (( $(tput colors 2>/dev/null) > 256 )); then
    echo "What a beautiful rainbow!!!"
else
    echo "Back to the 80's VGA era anyone?"
fi

Of course, tput only outputs the advertised capabilities as registered in the Terminfo database, so it might not accurately reflect your actual terminal capabilities.

Unfortunately, as per comments, many terminals still have wrong or outdated info, specially in platforms such as Windows and MacOS. Hopefully, given enough time (and bug reports!), such terminals will eventually set their $TERM appropriately. After all, it's been 4 years since the Terminfo database supports 24-bit colors.

6
  • 5
    The problem for a long while was that until 2018 terminfo did not reflect the number of colours available, which was in turn because the compiled binary format of the terminfo database only permitted 16-bit integers. It has had to adopt a new binary format. stackoverflow.com/a/36163656/340790
    – JdeBP
    Commented Aug 17, 2020 at 13:45
  • 4
    tput colors just reports 256 colors unless I set TERM=xterm-direct tput colors. Not reliable.
    – tastytea
    Commented Nov 27, 2020 at 6:23
  • @tastytea: if your terminal actually supports more than 256 colors with its default TERM, then its terminfo database is inaccurate, and that's a bug in your terminal. tput is the most consistent method to detect terminal capabilities in a portable way.
    – MestreLion
    Commented Jun 1, 2022 at 18:29
  • @MestreLion unfortunately it seems like many terminals are buggy in that regard. iTerm2 and Apple Terminal both set TERM=xterm-256color by default and don't appear to have a better option, even though iTerm2 definitely does support 24-bit color. Commented Sep 13, 2022 at 20:35
  • This fails on Windows Terminal. I got the second response while it appears to be displaying 24-bit colour when using this script: gist.github.com/weimeng23/60b51b30eb758bd7a2a648436da1e562
    – paradroid
    Commented Sep 19, 2022 at 15:41
1

I have cut this C++ solution from a program of mine.

First, set up termios so you can read one character at a time...

#include <termios.h>

  struct termios tio, tio_init;
  tcgetattr(0, &tio_init);         // Keep a copy so you can reset later
  tcgetattr(0, &tio);    
  tio.c_iflag &= ~(IXON | IXOFF);  // Pass Ctrl-S and Ctrl-Q
  tio.c_lflag &= ~(ECHO | ICANON); 
  tio.c_cc[VSUSP] = 0;             // Pass Ctrl-Z (confuses terminal)
  tio.c_cc[VMIN] = 0; 
  tio.c_cc[VTIME] = 1;
  tcsetattr(0, TCSANOW, &tio);

Here's a simple routine that submits an ANSI query, and pulls characters off the reply. We assume the terminal program is fast, and the 1-tick (0.1 sec) term character timeout will tell us when the reply characters stop coming. The terminating '\n' is sometimes needed to get the terminal program to execute the command. It may hang around until it decides a redraw us necessary.

void AnsiViewQuery(char *reply, const char *query, int size) {
  printf("%s\n", query);
  int len = 0;
  while (len < size-1) {
    if (read(0, reply+len, 1) == 1) ++len;
    else break;
  }
  reply[len] = '\0';
}

We set the foreground colour to 1;2;3, and then see if we can read it back again...

  char reply[32];
  printf("\e[38;2;1;2;3m");
  AnsiViewQuery(reply, "\eP$qm\e\\", 32);
  int truecolor = strncmp("\eP1$r0;38:2::1:2:3m", reply, 19) ? 0 : 1;

This is not foolproof. I want to set 24-bit colours but I do not need to read them back. This test passes if both writing and reading work. Some terminals such as the Gnome terminal support one but not the other. If it thinks truecolor is supported, it should be right. Maybe you can improve this.

The same program outputs a unicode character and checks the cursor has moved by one character, to check the particular unicode set is supported.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .