146

I am in the process of colorizing my terminal’s PS1.

I am setting color variables using tput; for example, here’s purple:

PURPLE=$(tput setaf 125)

Question:

How do I find the color codes (e.g. 125) of other colors?

Is there a color table guide/cheat sheet somewhere?

I’m just not sure what 125 is … Is there some way to take a hex color and convert into a number that setaf can use?

mhulse
  • 1,779

7 Answers7

274

The count of colors available to tput is given by tput colors.

To see the basic 8 colors (as used by setf in urxvt terminal and setaf in xterm terminal):

$ printf '\e[%sm▒' {30..37} 0; echo           ### foreground
$ printf '\e[%sm ' {40..47} 0; echo           ### background

And usually named as this:

Color       #define       Value       RGB
black     COLOR_BLACK       0     0, 0, 0
red       COLOR_RED         1     max,0,0
green     COLOR_GREEN       2     0,max,0
yellow    COLOR_YELLOW      3     max,max,0
blue      COLOR_BLUE        4     0,0,max
magenta   COLOR_MAGENTA     5     max,0,max
cyan      COLOR_CYAN        6     0,max,max
white     COLOR_WHITE       7     max,max,max

To see the extended 256 colors (as used by setaf in urxvt):

$ printf '\e[48;5;%dm ' {0..255}; printf '\e[0m \n'

If you want numbers and an ordered output:

#!/bin/bash
color(){
    for c; do
        printf '\e[48;5;%dm%03d' $c $c
    done
    printf '\e[0m \n'
}

IFS=$' \t\n'
color {0..15}
for ((i=0;i<6;i++)); do
    color $(seq $((i*36+16)) $((i*36+51)))
done
color {232..255}

256 color chart in sequence, labeled with their index


The 16 million colors need quite a bit of code (some consoles can not show this).
The basics is:

fb=3;r=255;g=1;b=1;printf '\e[0;%s8;2;%s;%s;%sm▒▒▒ ' "$fb" "$r" "$g" "$b"

fb is front/back or 3/4.

A simple test of your console capacity to present so many colors is:

for r in {200..255..5}; do fb=4;g=1;b=1;printf '\e[0;%s8;2;%s;%s;%sm   ' "$fb" "$r" "$g" "$b"; done; echo

red line, fading from darker to lighter (left-to-right) It will present a red line with a very small change in tone from left to right. If that small change is visible, your console is capable of 16 million colors.

Each r, g, and b is a value from 0 to 255 for RGB (Red,Green,Blue).

If your console type support this, this code will create a color table:

mode2header(){
    #### For 16 Million colors use \e[0;38;2;R;G;Bm each RGB is {0..255}
    printf '\e[mR\n' # reset the colors.
    printf '\n\e[m%59s\n' "Some samples of colors for r;g;b. Each one may be 000..255"
    printf '\e[m%59s\n'   "for the ansi option: \e[0;38;2;r;g;bm or \e[0;48;2;r;g;bm :"
}
mode2colors(){
    # foreground or background (only 3 or 4 are accepted)
    local fb="$1"
    [[ $fb != 3 ]] && fb=4
    local samples=(0 63 127 191 255)
    for         r in "${samples[@]}"; do
        for     g in "${samples[@]}"; do
            for b in "${samples[@]}"; do
                printf '\e[0;%s8;2;%s;%s;%sm%03d;%03d;%03d ' "$fb" "$r" "$g" "$b" "$r" "$g" "$b"
            done; printf '\e[m\n'
        done; printf '\e[m'
    done; printf '\e[mReset\n'
}
mode2header
mode2colors 3
mode2colors 4

chart of sample foreground colors with their index as labels

chart of sample background colors with their index as labels

To convert an hex color value to a (nearest) 0-255 color index:

fromhex(){
    hex=${1#"#"}
    r=$(printf '0x%0.2s' "$hex")
    g=$(printf '0x%0.2s' ${hex#??})
    b=$(printf '0x%0.2s' ${hex#????})
    printf '%03d' "$(( (r<75?0:(r-35)/40)*6*6 + 
                       (g<75?0:(g-35)/40)*6   +
                       (b<75?0:(b-35)/40)     + 16 ))"
}

Use it as:

$ fromhex 00fc7b
048
$ fromhex #00fc7b
048

To find the color number as used in HTML colors format:

#!/bin/dash
tohex(){
    dec=$(($1%256))   ### input must be a number in range 0-255.
    if [ "$dec" -lt "16" ]; then
        bas=$(( dec%16 ))
        mul=128
        [ "$bas" -eq "7" ] && mul=192
        [ "$bas" -eq "8" ] && bas=7
        [ "$bas" -gt "8" ] && mul=255
        a="$((  (bas&1)    *mul ))"
        b="$(( ((bas&2)>>1)*mul ))" 
        c="$(( ((bas&4)>>2)*mul ))"
        printf 'dec= %3s basic= #%02x%02x%02x\n' "$dec" "$a" "$b" "$c"
    elif [ "$dec" -gt 15 ] && [ "$dec" -lt 232 ]; then
        b=$(( (dec-16)%6  )); b=$(( b==0?0: b*40 + 55 ))
        g=$(( (dec-16)/6%6)); g=$(( g==0?0: g*40 + 55 ))
        r=$(( (dec-16)/36 )); r=$(( r==0?0: r*40 + 55 ))
        printf 'dec= %3s color= #%02x%02x%02x\n' "$dec" "$r" "$g" "$b"
    else
        gray=$(( (dec-232)*10+8 ))
        printf 'dec= %3s  gray= #%02x%02x%02x\n' "$dec" "$gray" "$gray" "$gray"
    fi
}

for i in $(seq 0 255); do
    tohex ${i}
done

Use it as ("basic" is the first 16 colors, "color" is the main group, "gray" is the last gray colors):

$ tohex 125                  ### A number in range 0-255
dec= 125 color= #af005f
$ tohex 6
dec=   6 basic= #008080
$ tohex 235
dec= 235  gray= #262626
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
  • Lovin’ fromhex. Thanks again! Also, I added a check for the #. Feedback? – mhulse Mar 12 '16 at 05:23
  • 1
    Yes, removing a leading '#' is a reasonable protection. I find a lot simpler to use hex=${1#"#"}. It will remove nothing if $1 does not have a #, and remove it if it exists. See my updated code. –  Mar 12 '16 at 07:03
  • Note that at least in my version of xterm the \e[0;%s8;2;%s;%s;%sm doesn't give me 16M colours, just the colour in the 240 colour palette that's closest to the requested rgb. – Stéphane Chazelas Mar 12 '16 at 08:49
  • @StéphaneChazelas Yes, the 16 million color do not work on all consoles (that is the reason for the note I added right from the start). I added now an specific test in red to "see" the (very small) change with that option. The terminal for KDE Konsole does work with 16 million color. –  Mar 12 '16 at 21:39
  • Of course, once "colour schemes" enter the picture, any notion of a standard palette for indexed colour, especially for the 16 aixterm colours, goes completely out of the window. As Thomas Dickey says in another answer, there is no universal colour table, because it depends from the terminal emulator software and from how that software happens to be configured today. – JdeBP Jan 09 '20 at 20:56
  • From trial and error, it seems that 48, as in \e[48;5;???] a special foreground code that unlocks 256 colors and changes the meaning of ;5; to be something other than bilnking for the background. Could someone please point to where that is documented? Ta. – Leo Jun 15 '20 at 07:02
  • Note that even though you don't need to wrap the escapes in \[ \] for printf, you do need to for PS1. – Leo Jun 15 '20 at 07:22
  • Excellent answer, however I found odd results if passing the zero-padded number returned by fromhex() to tput.
    Compare echo "$(tput setaf 46)i am green" with echo "$(tput setaf 046)i am green" Changing the printf line to printf '%3d' fixed this.
    – RCross Dec 24 '20 at 12:40
38

The tput utility is using a 256-color lookup table to print 8-bit ANSI escape sequences (starting with Esc and [) which makes use of terminal capabilities, so these control sequences can be interpreted as colors. These are pre-defined set of 256 colors used commonly across graphic cards.

To print all 256 colors in the terminal, try the following one-liner:

for c in {0..255}; do tput setaf $c; tput setaf $c | cat -v; echo =$c; done

Hint: Append | column to columnate list.

This 256-color lookup table can be also found at the Wikipedia page as follow:

Chart; ANSI escape code; 8-bit 256-color lookup table at Wikipedia; 256-color mode — foreground: ESC[38;5;#m   background: ESC[48;5;#m

kenorb
  • 20,988
19

The short answer is that you can find on the web tables of colors and match them up to the color number.

The long answer is that the correct mapping depends on the terminal —

The 125 is a parameter to an escape sequence referred to as setaf in the terminal description. tput attaches no particular meaning to the number. That actually depends upon the particular terminal emulator.

A while back, ANSI defined codes for 8 colors, and there were two schemes for numbering those. The two are seen in some terminal descriptions as the pairs setf/setb or setaf/setab. Since the latter has the connotation of "ANSI colors", you will see that used more often. The former (setf/setb) switched the order for red/blue as noted in the ncurses FAQ Why are red/blue interchanged?, but in either case, the scheme was established for just numbering the colors. There is no predefined relationship between those numbers and RGB content.

For specific terminal emulators, there are predefined color palettes which can be enumerated easily enough — and can be programmed using these escape sequences. There are no relevant standards, and you will see differences between terminal emulators, as noted in the xterm FAQ I don't like that shade of blue.

However, convention is often confused with standards. In development of xterm over the past 20 years, it incorporated ANSI (8) colors, adapted the aixterm feature (16) colors, added extensions for 88- and 256-colors. Much of that has been adopted by other developers for different terminal emulators. That is summarized in the xterm FAQ Why not make "xterm" equated to "xterm-256color"?.

The xterm source-code includes scripts for demonstrating the colors, e.g., using the same escape sequences that tput would use.

You may find this question/answer helpful as well: RGB values of the colors in the Ansi extended colors index (17-255)

Thomas Dickey
  • 76,765
  • Thank you so much for your help Thomas, I really appreciate it! I'm on a Mac/OS X running iTerm. Your explanation really helps me understand more about my setup (I've done a lot of copy/pasting from various other people's prompt colors on the web). I really appreciate you taking the time to write me a detailed and informative reply. :) – mhulse Mar 12 '16 at 03:04
15

ANSI Colors on console term

Preamble: about $TERM variable

Verify your environment with: tput colors

If you use some compatible xterm console, your $TERM may contain something like xterm*:

echo $TERM
xterm

tput colors
8

Further demos won't work: you will get image looking like:

enter image description here

unless you set this to xterm-256color:

export TERM=xterm-256color ; reset

tput colors
256

( You could even try to run this after setting export TERM=xterm-mono ;)

Doing simply the job by using tput

Depending on wich term protocol your console use, sequence could be: \e[38;5;XXXm or \e[3XXXm where XXX correspond to ansi number.

To ensure you use the right ANSI sequence, you have to use tput.

Regarding to Wikipedia's ANSI escape code, I wrote this:

#!/bin/bash

for ((i=0; i<256; i++)) ;do
    echo -n '  '
    tput setab $i
    tput setaf $(( ( (i>231&&i<244 ) || ( (i<17)&& (i%8<2)) ||
        (i>16&&i<232)&& ((i-16)%6 <(i<100?3:2) ) && ((i-16)%36<15) )?7:16))
    printf " C %03d " $i
    tput op
    (( ((i<16||i>231) && ((i+1)%8==0)) || ((i>16&&i<232)&& ((i-15)%6==0)) )) &&
        printf "\n" ''
done

Could render something like:

enter image description here

Optimizing : reducing forks by running tput as background process

... Then, because I hate runnig more than 200 forks in a little script, I wrote this:

#!/bin/bash

# Connector fifos directory
read TMPDIR < <(mktemp -d /dev/shm/conn_shell_XXXXXXX)

fd=3
# find next free fd
nextFd() { while [ -e /dev/fd/$fd ];do ((fd++)) ;done;printf -v $1 %d $fd;}

tputConnector() {
    mkfifo $TMPDIR/tput
    nextFd TPUTIN
    eval "exec $TPUTIN> >(LANG=C exec stdbuf -o0 tput -S - >$TMPDIR/tput 2>&1)"
    nextFd TPUTOUT
    eval "exec $TPUTOUT<$TMPDIR/tput"
    rm $TMPDIR/tput
    rmdir $TMPDIR
}
myTput() { echo -e "$1\ncr" 1>&$TPUTIN && IFS= read -r -d $'\r' -u $TPUTOUT $2
}
tputConnector

myTput op op
myTput "setaf 7" grey
myTput "setaf 16" black
fore=("$black" "$grey")
for ((i=0; i<256; i++)) ;do
    myTput "setab $i" bgr
    printf "  %s%s  %3d  %s" "$bgr" "${fore[ i>231 && i<244||(i<17)&& (i%8<2)||
        (i>16&&i<232)&&((i-16)%6*11+(i-16)/6%6*14+(i-16)/36*10)<58
        ? 1 : 0 ]}" $i "$op"
    (( ((i<16||i>231) && ((i+1)%8==0)) || ((i>16&&i<232)&& ((i-15)%6==0)) )) &&
        printf "\n" ''
done

With only 1 fork! Same result, but approx 10x faster!

If first script run in ~1.12 second on my desk, they take upto ~47.4 seconds on my raspberry-pi!!

While second script run in ~0.11 second on my desk and ~4.97 seconds on my raspberry.

  • xterm-256color is not universally applicable, and often an incorrect choice, of course. https://unix.stackexchange.com/a/560992/5132 – JdeBP Jan 09 '20 at 20:58
  • Of course, and putty is just one other implementation... I don't use putty. On my desk, I could use gnome-terminal, mate-terminal, konsole and xterm (my favorite) who all recognize xterm-256color. – F. Hauri - Give Up GitHub Jan 09 '20 at 21:17
  • No. They are not involved in any form of "recognition", and using xterm-256color for them is an incorrect choice, too. GNOME Terminal's correct terminal type is either gnome-256color or vte-256colour, for example. https://unix.stackexchange.com/a/515517/5132 – JdeBP Jan 09 '20 at 21:35
  • In addition to all of this, I've created a little [tag:bash] library hsvrgb.sh, with a hsvrgb browser and a hsvrgb demo. – F. Hauri - Give Up GitHub Sep 14 '21 at 07:24
  • BTW modern distribution include proper terminal definitions for putty: find /usr/share/terminfo/ -name '*putty*' | wc -l reports 21 on my system. And PuTTY itself sends its identity as xterm for wider compatibility, but can it send anything more appropriate if you change the default in Connection - Terminal-type string. putty-256color is a good start. – nponeccop Nov 14 '21 at 14:15
  • @nponeccop Correct, but as from one installation to other, it's a good thing to know this. – F. Hauri - Give Up GitHub Nov 14 '21 at 17:22
9

With zsh and in an xterm-like terminal (xterm and vte-based terminals like gnome-terminal, xfce4-terminal... at least), you can do:

$ read -s -t1 -d $'\a' $'c?\e]4;125;?\a' && echo "${c##*;}"
rgb:afaf/0000/5f5f

The bash equivalent:

read -s -t1 -d $'\a' -p $'\e]4;125;?\a' c && echo "${c##*;}"

(you want the escape sequence to query the colour to be sent after the terminal discipline echo is disabled (with -s) otherwise the response would be displayed by the line discipline half of the time, hence the sending of it as part of the read prompt (var?prompt in zsh like in ksh, -p prompt in bash)).

to get the definition of colour 125 (here as a RGB specification, each number being the intensity of the Red, Green and Blue components as a hex number between 0 and FFFF).

You can do the same for the first 16 colours with the xtermcontrol command:

$ xtermcontrol --get-color1
rgb:cdcd/0000/0000
  • Awesome, thank you so much for the help! +1 – mhulse Mar 12 '16 at 03:04
  • @Gilles, you want the query to be sent via the prompt after the terminal discipline echo is disabled. See edit. – Stéphane Chazelas Mar 12 '16 at 07:06
  • @StéphaneChazelas Using another terminal program (gnome-terminal) (which is a xterm terminal) I got your code to work correctly (both in bash and zsh) Oddly enough: tput colors reports only 8 even if the terminal is capable of presenting 256 colors. Also, the xterm-color (Konsole) tput colors only reports 8 even if that terminal is fully capable of presenting 16 million colors (and all 256 colors, of course). And no, no tmux or screen that may "color" :-) (change that is) the results (I was aware of that detail). In short: your code may fail in some terminal/consoles. –  Mar 12 '16 at 21:55
  • Ah, tohex added to my answer, A bit longer than what I was expecting, but the 256 colors have quite a bit of quirk turns. –  Mar 12 '16 at 22:02
  • @StéphaneChazelas, is there how to convert XTERM colour format to ANSI colour format? – Oo'- Feb 14 '22 at 03:34
3

I built on the answer from @kenorb to make the output fall into the columns that would create the gradient we are used to seeing.

#! /usr/bin/env bash

env -i
TERM="$TERM"
LESS='--RAW-CONTROL-CHARS --chop-long-lines --no-init'
bash -c ' { p(){ tput setaf "$@"; tput setaf "$@" | cat -v; echo ="$@"; } for c in {0..15}; do p "$c"; done | column -x -c 140; for c in {16..255}; do p "$c"; done | column -x -c 150; } | less'

The unnecessary semicolons are there because I developed this as a oneliner and it can easily be converted back as...

env -i TERM="$TERM" LESS='--RAW-CONTROL-CHARS --chop-long-lines --no-init' bash -c '{ p(){ tput setaf "$@"; tput setaf "$@"|cat -v; echo ="$@"; }; for c in {0..15}; do p "$c"; done|column -x -c 140; for c in {16..255}; do p "$c"; done|column -x -c 150; }|less'

NOTE: You can copy-pasta the oneliner above as a shortcut to seeing the output of the script.

0

There's the nice terminal-colors Python3 script. Here's what it gives on xfce4-terminal:

$ terminal-colors -n

terminal-colors output

sphakka
  • 145