Friday, November 19, 2021

Linux Command Prompt in Color

In this modern world, basically every terminal is compatible with VT-52 terminal sequences.  These includes Windows Terminal (free from Microsoft on the Microsoft Store), Terminal.app (on macOS) and most terminals available under Linux.  Further, most of these support emoji and 256 colors.

Yet, most of the advice out there on how to deal with this suggests hard-coding these escape sequences directly into your prompt.  This is great, and probably will work just fine, but I find it to be painful.

On the off-chance such a prompt is ever tried on a terminal that uses a different terminal command language, there's a decent chance the prompt will look like garbage.  Of course, linux has a whole dictionary of terminal types (terminfo) and a bunch of tools around that.

BASH Prompt: PS1

Generally, the PS1, or primary prompt for the user is set by the system in /etc/profile or one of the files in /etc/profile.d/, and a user can override that prompt in a ${HOME}/.bash_profile or .bashrc file.

A common default prompt is PS1='\u@\h:\w$ ' as in:

username@hostname:/home/username$ 

The important bit about embedding colors in a PS1 is to tell the shell that the characters it is sending out to the terminal are not actually advancing the cursor (this is important for command line editing, and long commands that wrap on the terminal).  Every command sent to the terminal needs to be wrapped in escaped brackets; '\[\]'.  Here is an embedded "reset to default" escape sequence within escaped brackets.

PS1='\[\033)B\033[0m\]\u@\h:\w$ '

TPUT

The 'tput' command is available on all Linux, and most UNIX systems.  My warning here is, like terminal escapes themselves, tput has its own weird language for settings.

setting a red foreground color using tput, and then setting it back, looks like this:

PS1='\u@\h:\w$ '
user@host:/home/user$ tput setaf 1
user@host:/home/user$ tput sgr0
user@host:/home/user$

In this example, the prompt isn't trying to control the colors yet, and we can see that the last color that the terminal was told to deal with become the default color for everything until something changes it.

My .bashrc

I have a chunk in my .profile where I literally set the colors and other attributes I can use.  If a terminal types does not have a command that matches one of these, then the shell variable will end up empty.
_TM_TX=$(tput setaf 7|sed -e 's/\x1b/\\033/g')
_TM_RD=$(tput setaf 9|sed -e 's/\x1b/\\033/g')
_TM_GR=$(tput setaf 10|sed -e 's/\x1b/\\033/g')
_TM_YL=$(tput setaf 11|sed -e 's/\x1b/\\033/g')
#_TM_BL=$(tput setaf 12|sed -e 's/\x1b/\\033/g')
_TM_BL=$(tput setaf 45|sed -e 's/\x1b/\\033/g')
_TM_PP=$(tput setaf 13|sed -e 's/\x1b/\\033/g')
_TM_TL=$(tput setaf 14|sed -e 's/\x1b/\\033/g')
_TM_ITAL=$(tput sitm|sed -e 's/\x1b/\\033/g')
_TM_BOLD=$(tput bold|sed -e 's/\x1b/\\033/g')
_TM__=$(tput sgr0|sed -e 's/\x1b/\\033/g') # RESET
if [ ! -z "$_TM_TX" ]
then
    _PS_TX="\[${_TM_TX}\]"
fi
if [ ! -z "$_TM_RD" ]
then
    _PS_RD="\[${_TM_RD}\]"
    _PS_GR="\[${_TM_GR}\]"
    _PS_YL="\[${_TM_YL}\]"
    _PS_BL="\[${_TM_BL}\]"
    _PS_PP="\[${_TM_PP}\]"
    _PS_TL="\[${_TM_TL}\]"
fi
if [ ! -z "$_TM_ITAL" ]
then
_PS_ITAL="\[${_TM_ITAL}\]"
fi
if [ ! -z "$_TM_BOLD" ]
then
    _PS_BOLD="\[${_TM_BOLD}\]"
fi
_PS__="\[${_TM__}\]"
PS1="${_PS_GR}\u${_PS__}@${_PS_BL}${_PS_BOLD}\h${_PS__}:${_PS_TL}\w${_PS__}\$ "

There's a repeating sed command here.  What this does is it takes any escape characters in the output, and replaces them with an escaped octal for the escape key.  This means that one can still echo $PS1 and see what the codes actually look like.

I'm using a light blue from the 255 color palette because I use a dark background on all of my terminals.  This also means I'm at risk of having no color at all for elements that I expect to be blue.

References

  • For tput subcommand info: see this terminfo manpage (using the cap-name column, and Color Handling).
  • For setaf (or, background: setab) values, I use this color chart from Wikipedia.
  • For more information on \[ and \], see the bash manpage, section on PROMPTING.


No comments:

Post a Comment