Posted by DaveV on Sun 7 Aug 2005 at 12:24
While most people never change their Bash prompt, some of us suffer from a mild form of insanity that drives us to configure every option as far as the system will let us. This article is an example of how far you can push what most people would consider a simple option and hopefully will start a discussion on other prompt customizations and tweaks.
NOTE: The Bash Prompt Howto can be found at:
http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/
This is the Bash prompt that I've been using to get a little more system status information on each line.
Save the script to a file and source it from your .bashrc.
#!/bin/bash
# Filename: custom_prompt.sh
# Maintainer: Dave Vehrs
# Last Modified: 12 Jul 2005 13:29:40 by Dave Vehrs
# Current Format: USER@HOST [dynamic section] { CURRENT DIRECTORY }$
# USER: (also sets the base color for the prompt)
# Red == Root(UID 0) Login shell (i.e. sudo bash)
# Light Red == Root(UID 0) Login shell (i.e. su -l or direct login)
# Yellow == Root(UID 0) priviledges in non-login shell (i.e. su)
# Brown == SU to user other than root(UID 0)
# Green == Normal user
# @:
# Light Red == http_proxy environmental variable undefined.
# Green == http_proxy environmental variable configured.
# HOST:
# Red == Insecure remote connection (unknown type)
# Yellow == Insecure remote connection (Telnet)
# Brown == Insecure remote connection (RSH)
# Cyan == Secure remote connection (i.e. SSH)
# Green == Local session
# DYNAMIC SECTION:
# (If count is zero for any of the following, it will not appear)
# [scr:#] ==== Number of detached screen sessions
# Yellow == 1-2
# Red == 3+
# [bg:#] ==== Number of backgrounded but still running jobs
# Yellow == 1-10
# Red == 11+
# [stp:#] ==== Number of stopped (backgrounded) jobs
# Yellow == 1-2
# Red == 3+
# CURRENT DIRECTORY: (truncated to 1/4 screen width)
# Red == Current user does not have write priviledges
# Green == Current user does have write priviledges
# NOTE:
# 1. Displays message on day change at midnight on the line above the
# prompt (Day changed to...).
# 2. Command is added to the history file each time you hit enter so its
# available to all shells.
# Configure Colors:
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
How about you? What interesting snippits or tricks do you have in your prompt?
I can see how your format would be useful but plain white with a long path on the end isn't ugly?
1. I like long directory names and so the path piece will often be longer than the screen width so I truncate it. If I need the path for a command, I can just call $PWD.
2. I find bash completion lets me tab-complete the base ssh/scp commands faster than grabbing the mouse to cut/paste espeically if you assign host based configurations in your ~/.ssh/config to account for different username, etc. For example:
(From ~/.ssh/config) Host work_email Hostname emailserver.mywork.com User dave ForwardAgent no Port 220
3. Given that a user should never execute a shell script that they do not understand from an untrusted source and given that your site includes a lot of shell scripts without similar warnings does this mean there is something in my prompt that you dont understand or concerns you? Or did you just pick this spot to throw out a random warning?
[ Parent ]
Perhaps I wasn't as careful as I should have been - what I meant to say wasn't "ugly", but "distracting". But as this is a matter of taste it is probably something I shouldn't have mentioned.
Bash completion for SSH / SCP comments isn't as useful as it could be, unless you have agents setup, or passwordless logins. Because whilst you can complete the remote hostnames you cannot complete the PATH. Unless I'm mistaken.
Whilst I take your point that users shouldn't execute anything that they don't trust. I was mostly mentioning this proactively because a lot of shell prompt examples, or samples, I've seen use control codes, or escape characters, in such a way that it's easy for malicious scripts to hide.
As a perfect example see comment #4. Whilst that isn't a malicious example it is completely unreadable to me - short of running it and that is something that users should be in the habit of considering.
So whilst it was an unfocussed warning, not aimed at you specifically, it wasn't randomly thrown in!
Steve
-- Steve.org.uk
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
piem@pomme:/home/piem
$ echo $PROMPT_COMMAND
echo -e "\033[${COLOR}${USER}@${HOSTNAME}\033[0;00m:${PWD}"
piem@pomme:/home/piem
$ echo $PS1
$
ciao, piem[ Parent ]
[ Parent ]
OK, I was inspired by this idea (from the Slashdot link Steve posted) and I added one more feature to my prompt.
URL: http://slashdot.org/comments.pl?sid=108424&cid=9219400
Now in the dynamic section, it will also list the exit status code of any application that exit with an error.
Simply add the following lines just above the Detached Screen Sessions comment and after the end of the previous if block:
...
fi
# Exit status of the last command run.
PS1="${PS1}\[\`let exitstatus=\$? ; if [[ \${exitstatus} != 0 ]] ; then echo \"\[${COLOR_RED}\][es:\${exitstatus}] \" ; fi\`\]"
# Detached Screen Sessions
...
[ Parent ]
[ Parent ]
This looks like a nice prompt generator:http://www.linuxhelp.net/guides/bashprompt/bashprompt-print.php
[ Parent ]
[ Parent ]
My prompt: PS1="\033[0;37m\u\[\033[0;32m\][\033[0;37m@\[\033[0;32m\]]\[\033[ 0;37m\]\h\[\033[0;32m\](\[\033[0;37m\]\t\[\033[0;32m\])\[\033[0;3 7m\]\[\033[0;32m\][\[\033[0;37m\]\w\[\033[0;32m\]]\[\033[0;37m\]\ n\[\033[0;32m\]$\[\033[0;37m\]"
[ Parent ]
red='\e[0;31m'
RED='\e[1;31m'
blue='\e[0;34m'
BLUE='\e[1;34m'
green='\e[0;32m'
GREEN='\e[1;32m'
cyan='\e[0;36m'
CYAN='\e[1;36m'
NC='\e[0m' # No Color
function powerprompt() {
_powerprompt() {
LOAD=$(uptime|sed -e "s/.*: \([^,]*\).*/\1/" -e "s/ //g")
TIME=$(date +%H:%M:%S)
OPENSHELLS=$(who|wc -l|sed -e "s/ //g")
UPTIME=$(uptime|sed -e "s/.*up\([^,]*\).*/\1/" -e "s/ //g")
}
PROMPT_COMMAND=_powerprompt
case $TERM in
xterm | dtterm | rxvt )
PS1="${cyan}[Time: ${green}\${TIME}${cyan} | OpenShells: ${green}\${OPENSHELLS}${cyan} | Load: ${green}\${LOAD}${cyan} | Uptime: ${green}\${UPTIME}${cyan}]${NC}\n[\#.][\u@\h]:\w> \[\033]0;[\u@\h] \w\007\
]" ;;
linux )
PS1="${cyan}[Time: ${green}\${TIME}${cyan} | OpenShells: ${green}\${OPENSHELLS}${cyan} | Load: ${green}\${LOAD}${cyan} | Uptime: ${green}\${UPTIME}${cyan}]${NC}\n[\#.][\u@\h]:\w>" ;;
* )
PS1="[Time: \${TIME} - Load: \${LOAD}]\n[\#.][\u@\h]:\w> " ;;
esac
}
[ Parent ]
[ Parent ]
For Screen users, this will change the screen window title (which we can use to change the xterm window title).
Replace the Titlebar segment with:
# Titlebar
case ${TERM} in
screen* )
local TITLEBAR='\[\033k\w\033\134\]'
;;
xterm* )
local TITLEBAR='\[\033]0;\u@\h: { \w } \007\]'
;;
* )
local TITLEBAR=''
;;
esac
Then we can enable a caption with tabs for each window and set the xterm title with the following added to your .screenrc
# Caption (use lastline for tabs)
caption always "%?%F%{-b gk}%:%{-b bb}%?%C | %D | %M %d |%H |%?%F%{-b gk}%? %L=%-Lw%45>%{-b kg}%n%f* %t%{-}%+Lw%-0<"
# Hardstatus (update xterm title)
hardstatus string "SCREEN @ %H: %-n - %t"
[ Parent ]
To track zombie processes, just add the following snippit between the Exit status and Detached screen sessions snippets:
# Zombie process counts
local ZOMBIES=$(ps hr -Nos | awk '$1=="Z" {print $1}' | wc -l)
if [ ${ZOMBIES} -gt 0 ]; then
PS1="${PS1}\[${COLOR_RED}\][Z:${ZOMBIES}] "
fi
Enjoy.
[ Parent ]
[ Parent ]
[ View Weblogs ]
I have to say that I find that prompt quite ugly! Still if you like it then I guess that's enough :)
The use of colour for conveying information is very nice - but having the components split up is quite distracting.
Personally I use the "standard" setup of having:
This is very useful for me because it allows me to cut and paste parts of the prompt for use in other shell windows. Usually that's one of the following commands:
Both of those I do sufficiently often that the thought of not including these bits of information like that is quite distracting.
I did think that I'd seen a discussion on shell prompts on Slashdot in the semi-recent past. The closest I could find was this poll on shell prompt length - there are some interesting comments there.
However it's worth cautioning users to not blindly type in arbitary shell propmt examples they see - those control codes and dense snippets sometimes contain badnesstm ...
Steve
-- Steve.org.uk
[ Parent ]