Blue Ice
Blue Ice

Reputation: 7930

ANSI Color Specific RGB Sequence Bash

I know that in bash terminals a reliable way to change color is using ANSI escape sequences. For example:

echo -e "\033[0;31mbrown text\033[0;00m"

should output

brown text (in brown)

Is there a way to output color using a specific RGB set with ANSI? Say I want bright red:

echo -e "**\033[255:0:0m**red text\033[0;00m"

Does this sort of thing exist?

I just want to use standard bash.

Upvotes: 58

Views: 53874

Answers (6)

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

Reputation: 71017

Playing with RGB (and HSV) in

ANSI sequences in terminal.

There are two way of printing colors in bash.

After playing with nice tools found on xterm's source tree, here is how vttests/256colors2.pl show on my gnome-terminal:

vttests/256colors2.pl

show 256 colors: 16 terminal colors + 6 * 6 * 6 RGB levels + 24 grayscales.

this use ANSI syntax \e[48;5;COLORm:

printf '\e[48;5;%sm' $color;

instead of \e[48;2;RED;GREEN;BLUEm:

printf '\e[48;2;%s;%s;%sm' $red $green $blue;

I've done some functions to play with RGB, and HSV:

RGB to HSV

hsv() {
    local -n _result=$4
    local -i _hsv_min _hsv_t
    local _hsv_s
    local -i _hsv_max=" $1 > $2 ?
                (_hsv_min=($2 > $3 ? $3:$2 ), ( $1 > $3 ? $1 : $3 )) :
                (_hsv_min=($1 > $3 ? $3:$1 ), $2) > $3 ? $2 : $3 "
    case $_hsv_max in 
        $_hsv_min) _hsv_t=0 ;;
        $1) _hsv_t=" ( 60 * ( $2 - $3 ) / ( _hsv_max-_hsv_min )+ 360 )%360";;
        $2) _hsv_t=" 60 * ( $3 - $1 ) / ( _hsv_max-_hsv_min )+ 120 " ;;
        $3) _hsv_t=" 60 * ( $1 - $2 ) / ( _hsv_max-_hsv_min )+ 240 " ;;
    esac
    _hsv_s=0000000$(( _hsv_max==0?0 : 100000000-100000000*_hsv_min / _hsv_max ))
    printf -v _hsv_s %.7f ${_hsv_s::-8}.${_hsv_s: -8}
    _result=($_hsv_t $_hsv_s $_hsv_max)
}

Then

RED=255 GREEN=240 BLUE=128
hsv $RED $GREEN $BLUE hsvAr
echo ${hsvAr[@]}
52 0.4980392 255
printf 'Hue: %d, Saturation: %f, Value: %d\n' "${hsvAr[@]}"
Hue: 52, Saturation: 0.498039, Value: 255

HSV to RGB

rgb() {
    local -n _result=$4
    local -i _rgb_i=" (($1%360)/60)%6 "
    local -i _rgb_f=" 100000000*($1%360)/60-_rgb_i*100000000 "
    local _rgb_s
    printf -v _rgb_s %.8f "$2"
    _rgb_s=$((10#${_rgb_s/.}))
    local -i _rgb_l=" $3*(100000000-_rgb_s)/100000000 "
    case $_rgb_i in
        0 )
            local -i _rgb_n="$3*(100000000-(100000000-_rgb_f)*_rgb_s/100000000)/
                                100000000 "
            _result=("$3" "$_rgb_n" "$_rgb_l") ;;
        1 )
            local -i _rgb_m=" $3*(100000000-_rgb_f*_rgb_s/100000000)/100000000 "
            _result=("$_rgb_m" "$3" "$_rgb_l") ;;
        2 )
            local -i _rgb_n="$3*(100000000-(100000000-_rgb_f)*_rgb_s/100000000)/
                                100000000 "
            _result=("$_rgb_l" "$3" "$_rgb_n") ;;
        3 )
            local -i _rgb_m=" $3*(100000000-_rgb_f*_rgb_s/100000000)/100000000 "
            _result=("$_rgb_l" "$_rgb_m" "$3") ;;
        4 )
            local -i _rgb_n="$3*(100000000-(100000000-_rgb_f)*_rgb_s/100000000)/
                                100000000 "
            _result=("$_rgb_n" "$_rgb_l" "$3") ;;
        * )
            local -i _rgb_m=" $3*(100000000-_rgb_f*_rgb_s/100000000)/100000000 "
            _result=("$3" "$_rgb_l" "$_rgb_m") ;;
    esac
}

Then

rgb 160 .6 240 out
echo ${out[@]}
96 240 192
printf '\e[48;5;%d;%d;%dm    \e[0m\n' "${out[@]}"

Will produce a bunch of colored spaces. one colored block

Further: hsvrgb-browser.sh

Preamble: Store previous two function into a file called hsvrgb.sh, stored in same directory than downloaded hsvrgb-browser.sh. (Note you could download

bash HSV-RGB browser

This picture show started interface run with argument: ./hsvrgb-browser.sh DeepSkyBlue3 (notice the little arrow behind named square mean shown color is exactly same than named color, not only closest). Then if you hit u (for Usage), you will see:

Keyboard interaction: 
               
  [RrGgBbVvLl] Incrase/decrase [R,G,B,Val|Lum] by step (3), from 0 to 255.
  [HhTt]       Incrase/decrase Hue (Tint) loop over 0 - 359.
  [Ss]         Increase/decrase Saturation by .002x step (3).
               
  [Cc]         Toggle Color bar rendering (upper C fix HSV)
               
  [p]          Paste color from clipboard
  [P]          Toggle loop mode Paste Color from clipboard every .3sec
  [k]          peeK color on screen
               
  [+-]         Incrase/decrase step. (3)
  [fF]         Refresh display (or ^L)
               
  [uU]         show Usage (this)
  [qQ]         Quit.
               
hsvrgb-browser.sh - hsv-rbg-browser.sh -- bash script for browsing RGB <-> HSV Colors. 

Note: I don't know why: initialization step take a lot of time under mate-terminal and gnome-terminal, but is near instantaneous by using Xterm.

Note: Regarding mmeisner' comment, If you encounter issue with this script, try to run them by:

LC_ALL=C.UTF8 ./hsvrgb-browser.sh

Important note: Ensure your terminal support 256colors! (Don't blame this if run under screen, tmux or any 16 color terminal!)

Upvotes: 4

Foxie Flakey
Foxie Flakey

Reputation: 420

This will work

echo -e "**\033[38;2;255;0;0m**red text\033[0;00m"

format: "\033[38;2;R;G;Bm"

  • R is your RED component of your RGB
  • G is your GREEN component of your RGB
  • B is your BLUE component of your RGB

Upvotes: 7

Both answers here fail to mention the Truecolor ANSI support for 8bpc color. This will get the RGB color the OP originally asked for.

Instead of ;5, use ;2, and specify the R, G, and B values (0-255) in the following three control segments.

\x1b[38;2;40;177;249m

To test if your terminal supports Truecolor:

printf "\x1b[38;2;40;177;249mTRUECOLOR\x1b[0m\n"

On my machine, XTerm happily outputted the correct color; although, terminals that are modeled after terminals that predate modern RGB color generally will not support truecolor - make sure you know your target before using this particular variant of the escape code.


I'd also like to point out the 38 and the ;5/;2 - Blue Ice mentioned that 38 routes and then 5 changes the color. That is slightly incorrect.

38 is the xterm-256 extended foreground color code; 30-37 are simply 16-color foreground codes (with a brightness controlled by escape code 1 on some systems and the arguably-supported 90-97 non-standard 'bright' codes) that are supported by all vt100/xterm-compliant colored terminals.

The ;2 and ;5 indicate the format of the color, ultimately telling the terminal how many more sequences to pull: ;5 specifying an 8-bit format (as Blue Ice mentioned) requiring only 1 more control segment, and ;2 specifying a full 24-bit RGB format requiring 3 control segments.

These extended modes are technically "undocumented" and are completely implementation defined. As far as I know and can research, they are not governed by the ANSI committee.


For the so inclined, the 5; (256 color) format starts with the 16 original colors (both dark/light, so 30-37 and 90-97) as colors 0-15.

The proceeding 216 colors (16-231) are formed by a 3bpc RGB value offset by 16, packed into a single value.

The final 24 colors (232-256) are greyscale starting from a shade slightly lighter than black ranging up to a shade slightly darker than white. Some emulators interpret these steps as linear increments from (256 / 24) on all three channels, though I've come across some emulators that seem to explicitly define these values.

Here is a Javascript function that performs such a conversion, taking into account all of the greys.

function rgbToAnsi256(r, g, b) {
    // we use the extended greyscale palette here, with the exception of
    // black and white. normal palette only has 4 greyscale shades.
    if (r === g && g === b) {
        if (r < 8) {
            return 16;
        }

        if (r > 248) {
            return 231;
        }

        return Math.round(((r - 8) / 247) * 24) + 232;
    }

    var ansi = 16
        + (36 * Math.round(r / 255 * 5))
        + (6 * Math.round(g / 255 * 5))
        + Math.round(b / 255 * 5);

    return ansi;
}

So in a way, you can calculate 256 ANSI colors from initial RGB values by reducing them from 8 to 3 bits in order to form a 256 encoded value in the event you want to programmatically do so on terminals that do not support Truecolor.

Upvotes: 107

Blue Ice
Blue Ice

Reputation: 7930

This does exist, but instead of the 16777216 (256^3) colors that the OP was looking for, there are 216 (6^3) equally distributed colors, in a larger set of 256 colors. Example:

echo -e "\033[38;5;208mpeach\033[0;00m"

This will output a pleasing sort of peach colored text.


Taking apart this command: \033[38;5;208m

The \033 is the escape code. The [38; directs command to the foreground. If you want to change the background color instead, use [48; instead. The 5; is just a piece of the sequence that changes color. And the most important part, 208m, selects the actual color.


There are 3 sets of colors that can be found in the 256 color sequence for this escape. The first set is the basic "candy" color set, or values 0-15. Then there is a cube of distributed colors, from 16-231. Lastly there is a detailed grayscale set from 232-255.

You can find a table with all of these values here: http://bitmote.com/index.php?post/2012/11/19/Using-ANSI-Color-Codes-to-Colorize-Your-Bash-Prompt-on-Linux#256%20(8-bit)%20Colors

Upvotes: 56

egmont
egmont

Reputation: 683

Currently true color escape sequences (\e[38;2;R;G;Bm) are supported by certain terminal emulators including gnome-terminal (with vte >= 0.36), konsole, and st [suckless].

The feature is not supported by certain others, e.g. pterm [putty], terminology [enlightenment], urxvt.

xterm is halfway in between: it recognizes the escape sequences, but rounds every color to the nearest one in the 256-color palette.

Upvotes: 3

Some programmer dude
Some programmer dude

Reputation: 409452

No there's not.

And to nitpick, those are technically not "ANSI escape sequences" but VT100 control codes (which were defined long before there were graphical terminals and terms like "RGB").

Upvotes: -1

Related Questions