More Useful and Interesting Bash Prompts

A while back, we listed 8 Useful and Interesting Bash Prompts. That turned out to be one of our most popular articles, and still generates user response and feedback. This time we’ve found a few more gems, as well as some handy tips and tricks for having the coolest shell prompt on the proverbial block.

Using These Prompts

Some of the following prompts are one-liners, and can be pasted directly into your terminal for (temporary) use. To make them permanent, you’ll have to paste the code into your .bashrc or .bash_profile.

The larger, more complex prompts such as Twtty should be saved to their own script file (such as myprompt.sh), and you place a line like

source myprompt.sh

in your .basrc file to “import” them.

Twtty Prompt

This is a two-line prompt that holds quite a bit of information including username, hostname, working directory, time, and command history number. Because of the technical and visual complexity, it’s best to place this prompt script in its own file, then source it from your .bashrc or .bash_profile as described above.

morebashprompts-twtty

function prompt_command {
 
TERMWIDTH=${COLUMNS}
 
#   Calculate the width of the prompt:
 
hostnam=$(echo -n $HOSTNAME | sed -e "s/[\.].*//")
#   "whoami" and "pwd" include a trailing newline
usernam=$(whoami)
cur_tty=$(tty | sed -e "s/.*tty\(.*\)/\1/")
newPWD="${PWD}"
#   Add all the accessories below ...
let promptsize=$(echo -n "--(${usernam}@${hostnam}:${cur_tty})---(${PWD})--" \
                 | wc -c | tr -d " ")
let fillsize=${TERMWIDTH}-${promptsize}
fill=""
while [ "$fillsize" -gt "0" ] 
do 
    fill="${fill}-"
	let fillsize=${fillsize}-1
done
 
if [ "$fillsize" -lt "0" ]
then
   let cut=3-${fillsize}
	newPWD="...$(echo -n $PWD | sed -e "s/\(^.\{$cut\}\)\(.*\)/\2/")"
fi
}
 
PROMPT_COMMAND=prompt_command
 
function twtty {
 
local GRAY="\[\033[1;30m\]"
local LIGHT_GRAY="\[\033[0;37m\]"
local WHITE="\[\033[1;37m\]"
local NO_COLOUR="\[\033[0m\]"
 
local LIGHT_BLUE="\[\033[1;34m\]"
local YELLOW="\[\033[1;33m\]"
 
case $TERM in
    xterm*)
        TITLEBAR='\[\033]0;\u@\h:\w\007\]'
        ;;
    *)
        TITLEBAR=""
        ;;
esac
 
PS1="$TITLEBAR\
$YELLOW-$LIGHT_BLUE-(\
$YELLOW\$usernam$LIGHT_BLUE@$YELLOW\$hostnam$LIGHT_BLUE:$WHITE\$cur_tty\
${LIGHT_BLUE})-${YELLOW}-\${fill}${LIGHT_BLUE}-(\
$YELLOW\${newPWD}\
$LIGHT_BLUE)-$YELLOW-\
\n\
$YELLOW-$LIGHT_BLUE-(\
$YELLOW\$(date +%H%M)$LIGHT_BLUE:$YELLOW\$(date \"+%a,%d %b %y\")\
$LIGHT_BLUE:$WHITE\$$LIGHT_BLUE)-\
$YELLOW-\
$NO_COLOUR " 
 
PS2="$LIGHT_BLUE-$YELLOW-$YELLOW-$NO_COLOUR "
}

One important thing to note about this prompt is that the horizontal bar scales to fit the width of your terminal, and that the working directory line will truncate itself when it gets too large.

If you have any problems with the above code snippet, the plain text version can be downloaded here.

High-Performance Minimalist Prompt

All the complexities of a fancy prompt like Twtty do take up extra CPU cycles, and while it’s certainly not going to grind your system to a halt, it can add up. If you want something that packs a lot of info without slowing you down, I suggest this:

morebashprompts-minimal

PS1="[\d | \T -> \w ...\$?]\n#"

One interesting thing to note here is the use of the variable $? at the end. This signifies the exit status of the last command entered. 0 means all is well, any other number usually indicates an error.

Rob’s Prompt

This is one of the more popular prompts floating around the Internet. The code is small and simple, yet it gives many of the features found in the larger, more complex examples.

morebashprompts-robs

PS1="\[\033[0;33m\][\!]\`if [[ \$? = "0" ]]; then echo "\\[\\033[32m\\]"; else echo "\\[\\033[31m\\]"; fi\`[\u.\h: \`if [[ `pwd|wc -c|tr -d " "` > 18 ]]; then echo "\\W"; else echo "\\w"; fi\`]\$\[\033[0m\] "; echo -ne "\033]0;`hostname -s`:`pwd`\007"

MegaFancyPrompt

The name is entirely deserved – this prompt is quite large and extremely fancy. It’s got color, titlebar control, user-awareness, proxy detection, screen detection, job count, and more. In short, just about every feature found in the other prompts we’ve mentioned can be found here, and quite a few others. If you want it ALL in your prompt, this is it.

morebashprompts-megafancy

Note: Not all features are visible at all times, so while it may not look like much in the screenshot, you can check out the full (documented, commented) script file here for details on how to get the visible parts you want.

COLOR_WHITE='\033[1;37m'
COLOR_LIGHTGRAY='033[0;37m'
COLOR_GRAY='\033[1;30m'
COLOR_BLACK='\033[0;30m'
COLOR_RED='\033[0;31m'
COLOR_LIGHTRED='\033[1;31m'
COLOR_GREEN='\033[0;32m'
COLOR_LIGHTGREEN='\033[1;32m'
COLOR_BROWN='\033[0;33m'
COLOR_YELLOW='\033[1;33m'
COLOR_BLUE='\033[0;34m'
COLOR_LIGHTBLUE='\033[1;34m'
COLOR_PURPLE='\033[0;35m'
COLOR_PINK='\033[1;35m'
COLOR_CYAN='\033[0;36m'
COLOR_LIGHTCYAN='\033[1;36m'
COLOR_DEFAULT='\033[0m'
 
# Function to set prompt_command to.
function promptcmd () {
    history -a 
    local SSH_FLAG=0
    local TTY=$(tty | awk -F/dev/ '{print $2}')
    if [[ ${TTY} ]]; then 
        local SESS_SRC=$(who | grep "$TTY "  | awk '{print $6 }')
    fi
 
    # Titlebar
    case ${TERM} in 
        xterm*  )  
            local TITLEBAR='\[\033]0;\u@\h: { \w }  \007\]'
            ;;
        *       )  
            local TITLEBAR=''                               
            ;;
    esac
    PS1="${TITLEBAR}"
 
    # Test for day change.
    if [ -z $DAY ] ; then
        export DAY=$(date +%A)
    else
        local today=$(date +%A)
        if [ "${DAY}" != "${today}" ]; then
            PS1="${PS1}\n\[${COLOR_GREEN}\]Day changed to $(date '+%A, %d %B %Y').\n"
            export DAY=$today
       fi
    fi
 
    # User
    if [ ${UID} -eq 0 ] ; then
        if [ "${USER}" == "${LOGNAME}" ]; then
            if [[ ${SUDO_USER} ]]; then
                PS1="${PS1}\[${COLOR_RED}\]\u"
            else
                PS1="${PS1}\[${COLOR_LIGHTRED}\]\u"
            fi
        else                               
            PS1="${PS1}\[${COLOR_YELLOW}\]\u"
        fi
    else
        if [ ${USER} == ${LOGNAME} ]; then     
            PS1="${PS1}\[${COLOR_GREEN}\]\u"
        else                               
            PS1="${PS1}\[${COLOR_BROWN}\]\u"
        fi
    fi
 
    # HTTP Proxy var configured or not
    if [ -n "$http_proxy" ] ; then
        PS1="${PS1}\[${COLOR_GREEN}\]@"
    else                               
        PS1="${PS1}\[${COLOR_LIGHTRED}\]@"
    fi
 
    # Host
 
    if [[ ${SSH_CLIENT} ]] || [[ ${SSH2_CLIENT} ]]; then 
        SSH_FLAG=1
    fi
    if [ ${SSH_FLAG} -eq 1 ]; then 
       PS1="${PS1}\[${COLOR_CYAN}\]\h "
    elif [[ -n ${SESS_SRC} ]]; then 
        if [ "${SESS_SRC}" == "(:0.0)" ]; then 
        PS1="${PS1}\[${COLOR_GREEN}\]\h "
        else 
            local parent_process=`cat /proc/${PPID}/cmdline`
            if [[ "$parent_process" == "in.rlogind*" ]]; then
                PS1="${PS1}\[${COLOR_BROWN}\]\h "
            elif [[ "$parent_process" == "in.telnetd*" ]]; then 
                PS1="${PS1}\[${COLOR_YELLOW}\]\h "
            else
                PS1="${PS1}\[${COLOR_LIGHTRED}\]\h "
            fi
        fi
    elif [[ "${SESS_SRC}" == "" ]]; then
        PS1="${PS1}\[${COLOR_GREEN}\]\h "
    else                                 
        PS1="${PS1}\[${COLOR_RED}\]\h " 
    fi
 
    # Detached Screen Sessions
    local DTCHSCRN=$(screen -ls | grep -c Detach )
    if [ ${DTCHSCRN} -gt 2 ]; then
        PS1="${PS1}\[${COLOR_RED}\][scr:${DTCHSCRN}] "
    elif [ ${DTCHSCRN} -gt 0 ]; then
        PS1="${PS1}\[${COLOR_YELLOW}\][scr:${DTCHSCRN}] "
    fi
 
    # Backgrounded running jobs
    local BKGJBS=$(jobs -r | wc -l )
    if [ ${BKGJBS} -gt 2 ]; then
        PS1="${PS1}\[${COLOR_RED}\][bg:${BKGJBS}]"
    elif [ ${BKGJBS} -gt 0 ]; then
        PS1="${PS1}\[${COLOR_YELLOW}\][bg:${BKGJBS}] "
    fi
 
    # Stopped Jobs
    local STPJBS=$(jobs -s | wc -l )
    if [ ${STPJBS} -gt 2 ]; then
        PS1="${PS1}\[${COLOR_RED}\][stp:${STPJBS}]"
    elif [ ${STPJBS} -gt 0 ]; then
        PS1="${PS1}\[${COLOR_YELLOW}\][stp:${STPJBS}] "
    fi
 
    # Bracket {
    if [ ${UID} -eq 0 ]; then              
        if [ "${USER}" == "${LOGNAME}" ]; then 
            if [[ ${SUDO_USER} ]]; then
                PS1="${PS1}\[${COLOR_RED}\]"
            else
                PS1="${PS1}\[${COLOR_LIGHTRED}\]"
            fi
        else                               
            PS1="${PS1}\[${COLOR_YELLOW}\]"
        fi
    else                                 
        if [ "${USER}" == "${LOGNAME}" ]; then 
            PS1="${PS1}\[${COLOR_GREEN}\]"
        else                               
            PS1="${PS1}\[${COLOR_BROWN}\]"
        fi
    fi
    PS1="${PS1}{ "
 
    # Working directory
    if [ -w "${PWD}" ]; then 
        PS1="${PS1}\[${COLOR_GREEN}\]$(prompt_workingdir)"
    else                                 
        PS1="${PS1}\[${COLOR_RED}\]$(prompt_workingdir)"
    fi
 
    # Closing bracket } and $\#
    if [ ${UID} -eq 0 ]; then              
        if [ "${USER}" == "${LOGNAME}" ]; then 
            if [[ ${SUDO_USER} ]]; then
                PS1="${PS1}\[${COLOR_RED}\]"
            else
                PS1="${PS1}\[${COLOR_LIGHTRED}\]"
            fi
        else                               
            PS1="${PS1}\[${COLOR_YELLOW}\]"
        fi
    else                                 
        if [ "${USER}" == "${LOGNAME}" ]; then 
            PS1="${PS1}\[${COLOR_GREEN}\]"
        else                               
            PS1="${PS1}\[${COLOR_BROWN}\]"
        fi
    fi
    PS1="${PS1} }\$\[${COLOR_DEFAULT}\] "
}     
 
# Trim working dir to 1/4 the screen width
function prompt_workingdir () {
  local pwdmaxlen=$(($COLUMNS/4))
  local trunc_symbol="..."
  if [[ $PWD == $HOME* ]]; then
    newPWD="~${PWD#$HOME}" 
  else
    newPWD=${PWD}
  fi
  if [ ${#newPWD} -gt $pwdmaxlen ]; then
    local pwdoffset=$(( ${#newPWD} - $pwdmaxlen + 3 ))
    newPWD="${trunc_symbol}${newPWD:$pwdoffset:$pwdmaxlen}"
  fi
  echo $newPWD
}
 
# Determine what prompt to display:
# 1.  Display simple custom prompt for shell sessions started
#     by script.  
# 2.  Display "bland" prompt for shell sessions within emacs or 
#     xemacs.
# 3   Display promptcmd for all other cases.
 
function load_prompt () {
    # Get PIDs
    local parent_process=$(cat /proc/$PPID/cmdline | cut -d \. -f 1)
    local my_process=$(cat /proc/$$/cmdline | cut -d \. -f 1)
 
    if  [[ $parent_process == script* ]]; then
        PROMPT_COMMAND=""
        PS1="\t - \# - \u@\H { \w }\$ "
    elif [[ $parent_process == emacs* || $parent_process == xemacs* ]]; then
        PROMPT_COMMAND=""
        PS1="\u@\h { \w }\$ "
    else
        export DAY=$(date +%A)
        PROMPT_COMMAND=promptcmd
     fi 
    export PS1 PROMPT_COMMAND
}
 
load_prompt

Bonus – Finding the Right Colors

As you may have noticed, most of these prompts use color, and the color is often represented in a coded form like ‘\033[1;30m’, which is quite difficult to read or remember. An easy way to see all your available colors, and preview what they’ll look like, is to simply paste the following snippet into your browser:

for i in `seq 1 7 ; seq 30 48 ; seq 90 107 ` ; do 
    echo -e "\e[${i}mtest\e[0m$i" 
done

Which will output each color code with its number.

morebashprompts-colortest

Conclusion

There are, of course infinite variations that could be packed into your prompt. Nearly any kind of textual data can be added in, and bash’s developers have had decades to add in functionality. Some readers have even commented about running custom Python or Ruby scripts from within the prompt. Whatever you do, make it functional, and if it’s REALLY cool, drop us a link in the comments!