How to show all colors supported by bash?
Asked Answered
N

3

4

One day, I typed the command

echo "\033[32mHELLOBASE\033[m"

in the gnome bash shell. The terminal showed me a green HELLOBASH string. I found this interesting. From my experience and serveral tests, I can change the number 32 from 0 up to 47. Next I wrote the following code,

for i in {0..48};do
    echo \033[$imHELLOBASH\[033m
done

Of course, it doesn't work, or I cannot be here! So how to improve the above code to function?

Nimitz answered 1/6, 2015 at 15:54 Comment(3)
Bash doesn't natively show colors at all -- your terminal does that. If you want to look up what your terminal can and can't support, the tput command looks up the right codes for colors.Cuprite
...the code you're using right now only works on some terminals -- on others, those codes would be garbage. Using tput means you always have the right code for the terminal type you're currently using.Cuprite
...see mywiki.wooledge.org/BashFAQ/037Cuprite
C
12

Let's do this the right way -- looking up color codes in our termcap (or, for modern systems, terminfo) database using the tput command:

for ((i=0; i<=48; i++)); do
  tput setaf "$i"
  echo HELLOBASH
done

If you want to see all available colors on a 256-color terminal, use this code token from BashFAQ #37:

colors256() {
        local c i j

        printf "Standard 16 colors\n"
        for ((c = 0; c < 17; c++)); do
                printf "|%s%3d%s" "$(tput setaf "$c")" "$c" "$(tput sgr0)"
        done
        printf "|\n\n"

        printf "Colors 16 to 231 for 256 colors\n"
        for ((c = 16, i = j = 0; c < 232; c++, i++)); do
                printf "|"
                ((i > 5 && (i = 0, ++j))) && printf " |"
                ((j > 5 && (j = 0, 1)))   && printf "\b \n|"
                printf "%s%3d%s" "$(tput setaf "$c")" "$c" "$(tput sgr0)"
        done
        printf "|\n\n"

        printf "Greyscale 232 to 255 for 256 colors\n"
        for ((; c < 256; c++)); do
                printf "|%s%3d%s" "$(tput setaf "$c")" "$c" "$(tput sgr0)"
        done
        printf "|\n"
}
colors256

For additional background on how and why any of this works, see the bash-hackers page on terminal codes.


As for why your original code didn't work even on terminals using ANSI color codes, by the way -- @rici pegged it correctly: Your parameter expansion was ambiguous without adding curly braces.

That is to say:

$imHELLOBASH

...needed to be...

${i}mHELLOBASH

...to avoid the shell trying to find and expand a variable called imHELLOBASH rather than a variable named i.

Cuprite answered 1/6, 2015 at 15:58 Comment(4)
termcap or terminfo? I think that is implementation dependant.Colb
Depends on how modern your system is. These days, yes, it would be terminfo.Cuprite
I thought termcap died out in the late 1980s, my recollection of the days of curses is a little shaky. Where the heck did you dig-up that program from? I'll bet you had to blow a lot of dust off it :-)Colb
They were both being distributed when I started in the mid 90s.Cuprite
C
3

It is not much to do with bash, more to do with the terminal driver. Rather than trying to use control characters, I suggest you use tput instead, for example:

i=1
while (( $i < 10 ))
do
    tput setaf $i
    echo "This is $i"
   (( i++ ))
done
Colb answered 1/6, 2015 at 16:2 Comment(0)
B
0

A simple version of your loop might look something like this:

for i in {0..48}; do
  echo "$(tput setaf $i)HELLOBASH$(tput sgr0)"
done

If you prefer to use ANSI color codes rather than spawn a subshell to call tput, then you could go this way:

for i in {0..48}; do
  echo $'\E[38;5;'$i'm'HELLOBASH$'\E[00m'
done

If you want something more readable, you can use variables:

for i in {0..48}; do
  fg=$'\E[38;5;'$i'm'
  reset=$'\E[00m'
  echo "${fg}HELLOBASH${reset}"
done

Powerlevel10k has a handy one-liner for Zsh users to see a colormap.

I use a modified version of that in Bash and it works great. The only overly clever bit in this snippet is the first 16 colors are special. They take up 8 per line because it looks nicer to line them up that way, and then after that the color sequences need to go in a 6-per line gradient, so there's a bit of modulo/echo magic to make that happen.

function colormap() {
  local i bg fg reset
  reset=$'\E[00m'
  for i in {0..255}; do
    fg=$'\E[38;5;'$i'm'
    bg=$'\E[48;5;'$i'm'
    printf "%s  %s"    "$bg" "$reset"
    printf "%s%03d%s " "$fg" "$i" "$reset"
    (( i <= 15 && (i + 1)  % 8 == 0 )) && echo
    (( i > 15  && (i - 15) % 6 == 0 )) && echo
  done
}

The advantage of this is that it's simple, and way faster than making 256 calls to tput.

Burget answered 27/8 at 12:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.