Tagged "tips"

Listing Terminal Colors

If you’ve ever worked with colors in a modern terminal emulator you may occasionally find yourself looking for just the right index to use in its standard 256-color palette. The other day I was in the same situation, so I wrote a little Bash script to print a pretty preview of all the colors. Here’s some sample output from Terminal.app (using the “Novel” profile):

256 terminal colors

Not bad! Now you could just download the script and call it a day, but what’s the fun in that? 😊 If you’re game, let’s take a few minutes to go through the code together and figure out how it works…

First, we write a function to print each color index:

 7padding="   "
 8
 9print_index () {
10  index=$1
11  printf "%s%s" "${padding:${#index}}" " $index "
12}

On line 7, we create a new string (padding) of three whitespace characters. On line 10, we set the variable index equal to the first parameter ($1). Finally, on line 11, we print the index padded to a width of five characters. The result is right-justified, with a single space to the right of each index.

The syntax for the padding is a bit obtuse, so let’s break it down:

  • ${variable} is a more explicit way of writing $variable, and simply returns the value of the given variable.
  • ${#variable} returns the length of the given variable. Because Bash variables are just character strings, this returns the number of digits in the index.
  • ${variable:offset} returns the value (substring) of the given variable starting from the specified offset.

So ${padding:${#index}} returns a string with the number of whitespace characters in padding less the number of digits in index. More specifically, this expression returns two whitespace characters if index has one digit; one whitespace character if index has two digits; and an empty string if index has three digits.

(For more details on this syntax, see the section on parameter substitution in the Advanced Bash-Scripting Guide.)

Next, we write a function to print each color:

14lut="\e[48;5;"
15reset="\e[0;39;49m"
16
17print_color () {
18  index=$1
19  printf "${lut}${index}m ${padding} ${reset}"
20}

On line 14, \e[48;5; is the start of an ANSI escape sequence, where

Similarly, on line 15, \e[0;39;49m is an SGR control sequence that resets all display attributes (0) before setting the default foreground (39) and background (49) colors.

(Note that the numbers in an SGR sequence are separated by semicolons and that a valid sequence must end in the ASCII character m.)

On line 19, we bring this all together to display a color patch. Building on the explanation above, we can describe what the expression ${lut}${index}m ${padding} ${reset} does as follows:

  • ${lut}${index}m expands to \e[48;5;${index}m, selecting the color at the given index from a 256-color lookup table.
  • ␣${padding}␣ expands to five whitespace characters (the width of the color patch).
  • ${reset} expands to \e[0;39;49m, resetting all display attributes along with the foreground and background colors.

Now that we have our plumbing in place, let’s print the eight standard colors (indices 0–7) on a single line:

26bold="\e[1m"
27
28printf "\n${bold}Standard colors${reset}\n\n"
29for (( n=0; n<8; n+=1 )) ; do
30  print_index $n
31  print_color $n
32done

(Note that we’ve introduced a new SGR control sequence, \e[1m, to make the heading text bold.)

We can print the eight “bright” colors (indices 8–15) the same way:

38printf "\n\n${bold}Bright colors${reset}\n\n"
39for (( n=8; n<16; n+=1 )) ; do
40  print_index $n
41  print_color $n
42done

Next, let’s print the 216 colors (indices 16–231) in the middle of the lookup table:

46printf "\n\n${bold}Colors #16-231${reset}\n"
47for (( n=16; n<232; n+=6 )) ; do
48  (( (n-16) % 36 )) || echo ""
49
50  for i in {0..5}; do
51    print_index $(( n+i ))
52    print_color $(( n+i ))
53  done
54
55  echo ""
56done

216 colors won’t really fit on a single line, so we’ll take the fact that they are arranged in a 6x6x6 “cube” into consideration and print them in 6x6 blocks, instead. We iterate through the indices six at a time (line 47), adding an extra space before each 6x6 block (line 48) and printing each index followed by a patch of its corresponding color (lines 50–53).

Finally, let’s print the 24 grayscale colors (indices 232–255):

60printf "\n${bold}Grayscale colors${reset}\n\n"
61for (( n=232; n<255; n+=6 )) ; do
62  for i in {0..5}; do
63    print_index $(( n+i ))
64    print_color $(( n+i ))
65  done
66
67  echo ""
68done

Once again, 24 colors won’t really fit on a single line, so we iterate through the indices six at a time (line 61), printing each index followed by a patch of its corresponding color (lines 62–65).

And that’s it! I hope you found this post to be helpful and/or edifying. Once again, you can download a copy of the script to use and modify as you see fit. Have a question or comment? Feel free to get in touch on Twitter or by email.

Happy coding! πŸ‘‹

Git Tip of the Day: --color-words

I’ve always felt that Git’s default diff output left something to be desired, especially when it was applied to Markdown text files (like the ones used to generate this blog). Line-by-line diffs aren’t very helpful if every line represents a paragraph of text.

I was thus pleasantly surprised to stumble across this blog post, which—among other things—explains how to generate word-by-word diffs that wrap nicely. The first step is to configure the default Git pager to wrap lines using one of the following commands.

git config --global core.pager less -r
git config --global core.pager "less -+\$LESS -FRX"

The second step is to tell Git to generate a (colored!) word-by-word diff with the --color-words option.

git diff --color-words

And that’s all there is to it! Note that --color-words is technically equivalent to --word-diff=color, so if you’re interested in reading about the other available options to --word-diff you can check out the git-diff man page.

As helpful as this technique is, there is one caveat to keep in mind: as far as I can tell the --color-words option only works with the git-diff and git-log commands. If you happen to use git add -p to stage individual changes—like I do—you are stuck with the default line-by-line diff output of git-add. That’s really only a minor inconvenience, though, because you can still call git diff --color-words just before git add -p to get a nicely-formatted overview of your changes before you stage them.

I hope you found this tip to be as helpful as I did!