Just a Rum-Soaked Space Hobo with an Inordinate Fondness for Makefiles

Hello, I'm from the London Permacomputing Group. I've been using Unix and The Internet since the 1980s, and Linux since the mid-90s. I like small open software I can wrap my head around, but I'm not a fan of those "suckless" fash types.

Prompt Engineering

Check out my neat bash prompt, which I put in eik's /etc/bash_completion.d/prompt.sh so everyone gets it:

The $PS1 text expansion environment is fairly limited. It can do parameter expansion, reference expansion, arithmetic expansion, and a couple other neat tricks. You can shell out, but if you do that it can overwrite the variable that contains the last exit code ($?). A lot of heavyweight systems like powerline or starship will tuck this data away before running loads of programs every time you hit return at the shell. I wanted to see if I could do more with less.

Tools such as the $(__git_ps1) hacks do their best to only use features of bash that work internally to bash itself. They make internal functions that try not to fork off processes if they can help it. I was inspired by the Debian $PS1's handling of their $debian_chroot variable, and decided to try and make a dynamic prompt that only shows information if it's relevant.

Implementation Details

This prompt uses the _noop array (with only ${_noop[0]} defined) to turn arithmetic expressions into defined/undefined responses. This lets us use some of the brace-expansion features to specify alternate output, so we can display the error code or number of jobs in the background only if they're non-zero, and we can style them with a high degree of freedom.

It also only displays the hostname if the $XDG_SESSION_TYPE is "tty" or if we have variables set by ssh, sudo, or doas. It dynamically displays the $SUDO_USER and $DOAS_USER, if either is defined.

Behaviour

What this means is that when you log in on your local desktop system, your prompt is merely:

~$

But the same prompt on a remote system will show the more common:

user@hostname:~$

And if you are sudo'd in a chroot with two background processes while looking at a git repo after a command that exited with an error code of 1:

(1)[2](thechroot)root<user>@hostname:/home/user/src/therepo{main $%}#

Each of these dynamic pieces of information will be bold and colour-coded, if tput setaf 1 was successful on your terminal or if your $TERM seems likely to support it. Otherwise you get a plain text version.

Here's the plain text version so you can see how it works:

unset _tty
_tty=$SSH_CONNECTION$SUDO_USER$DOAS_USER
[ "$XDG_SESSION_TYPE" = "tty" ] && _tty="yes"
# We use defined/undef logic on array indices to detect non-zero values inside
# the prompt, which is faster than shelling out
_noop[0]='array accesses turn 0/nonzero into defined/undef!'
  PS1='${_noop[$(($?==0))]:+($?)}${_noop[$((\j==0))]:+[\j]}'
  PS1+='${debian_chroot:+($debian_chroot)}'
  PS1+='${_tty:+\u${DOAS_USER:+<$DOAS_USER>}${SUDO_USER:+<$SUDO_USER>}@}'
  PS1+='${SSH_CONNECTION:+\h:}\w'
  type -t __git_ps1 > /dev/null && PS1+='$(__git_ps1 "{%s}")'
  PS1+='\$ '

Here's how we detect colour support

Most terminals support the standard ANSI escape sequences now, but we still have to be careful. This catches ghostty, kitty, foot, and any terminal type with color in the name (such as tmux-256color or xterm-256color-bce).

case "$TERM" in 
    *[is]tty*|foot*|*color*) color_prompt=yes;; 
    *) [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null && color_prompt=yes;;
esac

If it doesn't find your terminal in that list, and the system you're on has ncurses installed, then it will try to send the code for red text to /dev/null with the tput command, and keep you colour-free if that fails.

I just think it's neat!