Published in Articles on by Michiel van Oosterhout ~ 5 min read

In the previous article we learned that the Git for Windows distribution is based on MSYS2, a POSIX-like environment for Windows, and comes with additional software, including the Bash command-line shell. In this article we will start the work of configuring this installation of Bash. The ultimate goal is to configure a custom prompt. The first step towards that goal is to make it easy to change the colors of the output.

Colors in Bash

Git for Windows does not include a terminal, so its installation of Bash (added to the Start menu as Git Bash) runs in Windows' default terminal, which is either the traditional Console Window Host or the newer Windows Terminal (see The Windows command-line interface, part 2).

Bash, being an implementation of the POSIX shell, does not use Windows' Console API. Instead it relies on terminal sequences for things like colored output. These sequences start with the ESC control code, and are not so easy to type.

In Bash, input is handled by the Readline library, a library for line-editing. To type the ESC control code in Bash we must first use the quoted-insert Readline command, which by default is bound to Ctrl+V. This command signals that the next keypress should be interpreted verbatim. Typing Esc now will result in the ESC control code. This is how you can enter echo '^[[41;97mTEST^[[0m' to test a terminal sequence.

Alternatively we can use ANSI-C quoting which replaces \e with the ESC control code, so we can enter echo $'\e[41;97mTEST\e[0m'.

Finally, instead of an ANSI-C quoted string, the echo command's -e option can also be used to replace \e with the ESC control code, so we can enter echo -e '\e[41;97mTEST\e[0m'.

Terminal sequence input and output in Git Bash running in Console Window Host
Terminal sequence input and output in Git Bash running in Console Window Host

A color function for Bash

Windows Command Processor has a builtin color command to change the background and foreground colors of the entire buffer. This command expects colors to be specified using color palette entry numbers.

The `color` command in Windows Command Processor
The color command in Windows Command Processor

We can create a color function for Bash. But instead of changing the colors of the entire buffer, it will just output a terminal sequence to change the colors of the output immediately following the call to color. And instead of palette entry numbers, it will accept a letter for each color (r, g, b, c, m, y, k, and w) , as well as 0 for the default color, and - for the terminal's separate background color.

The Windows PowerShell script below creates a Bash file that, when sourced in a Bash session, makes the color function available.

# Set an environment variable for the terminal's background color
cmd /c setx TERM_BACKGROUND_COLOR "14;16;25"
cmd /c setx TERM_FOREGROUND_COLOR "255;250;244"

# Declare the Bash script contents
$script = @'
# Set background and foreground color
function color {
    # Colors (normal and bright)
    declare -A colors=(
        [k]=0  [r]=1  [g]=2  [y]=3  [b]=4  [m]=5  [c]=6  [w]=7
        [K]=60 [R]=61 [G]=62 [Y]=63 [B]=64 [M]=65 [C]=66 [W]=67
        [0]=9
    )

    # Start the SGR terminal sequence
    local seq=$'\e['

    # Parse background color
    local bg=${1:0:1}
    if [ $bg = - ]; then
        # Parameters for RGB color
        seq+="48;2;$TERM_BACKGROUND_COLOR"
    elif [ $bg = + ]; then
        # Parameters for RGB color
        seq+="48;2;$TERM_FOREGROUND_COLOR"
    else
        # Parameter for palette-based background color
        seq+=$(( ${colors[$bg]} + 40 ))
    fi

    # Separate the parameters for background color and foreground color
    seq+=';'

    # Parse foreground color
    local fg=${1:1:1}
    if [ $fg = - ]; then
        # Parameters for RGB color
        seq+="38;2;$TERM_BACKGROUND_COLOR"
    elif [ $fg = + ]; then
        # Parameters for RGB color
        seq+="38;2;$TERM_FOREGROUND_COLOR"
    else
        # Parameter for palette-based foreground color
        seq+=$(( ${colors[$fg]} + 30 ))
    fi

    # Finish the SGR terminal sequence
    seq+=m

    # Output the SGR terminal sequence
    echo -n $seq
}
'@

# Ensure the directory exists
$path = "$Env:LOCALAPPDATA\Commando\Color"
$_ = New-Item $path -Force -ItemType Directory

# Create the Bash script
$path = "$path\color.bash"
Set-Content -Path $path -Value $script

# Ensure proper line endings
((Get-Content $path) -join "`n") + "`n" | Set-Content -NoNewline $path

To use the function, source the script in a Git Bash session: . $LOCALAPPDATA/Commando/Color/color.bash and then call the function, e.g. color rW to set the background color to red and the foreground color to bright white.

Using the `color` function to output SGR terminal sequences in Git Bash
Using the color function to output SGR terminal sequences in Git Bash

Conclusion

Remembering and typing terminal sequences to change output colors (^[[41;67m) can be quite difficult. A relative simple Bash script can provide a more intuitive function instead (color rW), and works in Git Bash, Cygwin's Bash, and even WSL.

Updates

  • Added support for + to specify the terminal's custom foreground color.