#!/usr/bin/env bash ## CHECK THAT WE'RE RUNNING BASH if [ -z "$BASH_VERSION" ]; then echo 'This prompt only works in bash!' >&2 return 1 fi ## DON'T RUN TWICE if [[ "$_PROMPT_SET" = "true" ]]; then _prompt_warning 'Prompt already set!' return 0 fi _PROMPT_SET=true declare -r _PROMPT_SET ## FUNC function _fr { [[ -f "$1" ]] && [[ -r "$1" ]]; } function _frs { _fr "$1" && source "$@"; } function _dr { [[ -d "$1" ]] && [[ -r "$1" ]]; } ## ENVIRONMENT VARIABLES (defaults, override in ~/.bash_prompt-settings) _PROMPT_POWER=false # enable display of battery life in the prompt - this is overridden in the _prompt_setup function below. changing this here is futile _PROMPT_LEFT="(" # set the left bracket to [ or something if you wish _PROMPT_RIGHT=")" # set the right bracket to ] or something if you wish _PROMPT_HARDCODED_COLORS=false # set to true if tput fails for your terminal _PROMPT_EXITSTATUS=true # enable printing the exit status of the previous command _PROMPT_DATEFORMAT="%Y-%m-%d %H:%M:%S %Z" # "date"-compatible formatting string _PROMPT_DOMAIN=false # if "false", will cut off any @... part of the username ## DEBUG TRAP FUNCTIONS _PROMPT_DEBUGTRAP=true # enable the prompt debug trap (this is REQUIRED for the rest of these options to work) _PROMPT_TITLE_XTERM=false # puts the current command as part of the window status in xterm and compatibles _PROMPT_TITLE_SCREEN=true # puts the current command as the window name in screen _PROMPT_TITLE_TERMVAR=false # use the $TERM variable as part of the title _PROMPT_TIMER=true # enable automatic command timer for commands longer than _PROMPT_TIMERLIMIT _PROMPT_TIMERLIMIT=60 # used by the timer. execution times smaller than this are not reported ## LOAD USER SETTINGS _frs "${HOME}/.bash_prompt-settings" || : # the debug trap requires bash 4 to work properly if ((BASH_VERSINFO[0] < 4)); then _PROMPT_DEBUGTRAP=false fi # disable python virtualenv prompt - we do this ourselves export VIRTUAL_ENV_DISABLE_PROMPT=1 ## COMMAND TIMER FUNCTIONS if $_PROMPT_DEBUGTRAP; then function _prompt_debug_command { local command=${BASH_COMMAND%% *} if $_PROMPT_TIMER; then _prompt_timer_start fi if [[ -z "${_PROMPT_BASH_COMMAND:-}" ]]; then _PROMPT_BASH_COMMAND=${command##*/} if $_PROMPT_TITLE_XTERM; then _prompt_title_xterm fi if $_PROMPT_TITLE_SCREEN; then _prompt_title_screen fi fi } if $_PROMPT_TIMER; then function _prompt_timer_start { _PROMPT_SECONDS=${_PROMPT_SECONDS:-$SECONDS} } function _prompt_timer_stop { _PROMPT_SECONDS=$(( SECONDS - ${_PROMPT_SECONDS:-$SECONDS} )) } function _prompt_seconds2days { printf "%d days, %02d:%02d:%02d" \ $(( $1 / 86400 )) \ $(( $1 / 3600 % 24 )) \ $(( $1 / 60 % 60 )) \ $(( $1 % 60 )) | sed 's/^1\ days/1\ day/;s/^0\ days,\ //;s/^00://;s/^0//' } fi fi ## PRINT COLORED WARNINGS function _prompt_warning { echo -e "$(sed 's/\\\[//g; s/\\\]//g' <<<"${_PROMPT_COLOR_WARNING}")PROMPT: ${1}$(sed 's/\\\[//g; s/\\\]//g' <<<"${_PROMPT_COLOR_RESET}")" >&2 } ## TITLES function _prompt_title_xterm { local prompt_term if $_PROMPT_TITLE_TERMVAR; then prompt_term="$TERM" else prompt_term="" fi local title="${SSH_CONNECTION:+"ssh: "}${prompt_term:+"${prompt_term}: "}${USER}@${HOSTNAME}:${PWD}" if $_PROMPT_DEBUGTRAP && $_PROMPT_TITLE_XTERM then title="${_PROMPT_BASH_COMMAND:+$_PROMPT_BASH_COMMAND [}${title}${_PROMPT_BASH_COMMAND:+]}" fi case "$TERM" in *xterm*|*rxvt*|Eterm|mlterm) printf '\e]0;%s\a' "$title" #echo -ne "\e]0;${title}\a" ;; *) ;; esac } function _prompt_title_screen { if $_PROMPT_DEBUGTRAP && $_PROMPT_TITLE_SCREEN && [[ "$TERM" = "screen"* ]] then local title="${_PROMPT_BASH_COMMAND:-"bash"}" # shellcheck disable=SC1003 printf '\ek%s\e\\' "$title" #echo -ne "\ek${title}\e\\" fi } ## SETUP FUNCTION - DETERMINES COLORS AND TWEAKS function _prompt_setup { # we have to use hardcoded colors if tput is not available, or not working if ! $_PROMPT_HARDCODED_COLORS; then local tput="tput" if [[ "$(uname -s 2>/dev/null)" = "FreeBSD"* ]] && [[ -e /usr/local/bin/tput ]]; then tput=/usr/local/bin/tput fi if { ! type $tput &>/dev/null || ! $tput sgr0 &>/dev/null || [[ -z "$($tput sgr0)" ]]; }; then _PROMPT_HARDCODED_COLORS=true fi fi local reset black red green orange blue magenta cyan light_gray dark_gray light_red light_green yellow light_blue light_magenta light_cyan white bold if $_PROMPT_HARDCODED_COLORS; then # format: =$'\\[\e[0-1;30-37;40-47m\\]' # dark/bright ; fg color ; bg color (00 for any of them = reset) reset=$'\\[\e[m\\]' black=$'\\[\e[0;30m\\]' red=$'\\[\e[0;31m\\]' green=$'\\[\e[0;32m\\]' orange=$'\\[\e[0;33m\\]' blue=$'\\[\e[0;34m\\]' magenta=$'\\[\e[0;35m\\]' cyan=$'\\[\e[0;36m\\]' light_gray=$'\\[\e[0;37m\\]' dark_gray=$'\\[\e[1;30m\\]' light_red=$'\\[\e[1;31m\\]' light_green=$'\\[\e[1;32m\\]' yellow=$'\\[\e[1;33m\\]' light_blue=$'\\[\e[1;34m\\]' light_magenta=$'\\[\e[1;35m\\]' light_cyan=$'\\[\e[1;36m\\]' white=$'\\[\e[1;37m\\]' else reset="\\[$($tput sgr0)\\]" black="${reset}\\[$($tput setaf 0)\\]" red="${reset}\\[$($tput setaf 1)\\]" green="${reset}\\[$($tput setaf 2)\\]" orange="${reset}\\[$($tput setaf 3)\\]" blue="${reset}\\[$($tput setaf 4)\\]" magenta="${reset}\\[$($tput setaf 5)\\]" cyan="${reset}\\[$($tput setaf 6)\\]" light_gray="${reset}\\[$($tput setaf 7)\\]" bold=$($tput bold) dark_gray="${reset}\\[${bold}$($tput setaf 0)\\]" light_red="${reset}\\[${bold}$($tput setaf 1)\\]" light_green="${reset}\\[${bold}$($tput setaf 2)\\]" yellow="${reset}\\[${bold}$($tput setaf 3)\\]" light_blue="${reset}\\[${bold}$($tput setaf 4)\\]" light_magenta="${reset}\\[${bold}$($tput setaf 5)\\]" light_cyan="${reset}\\[${bold}$($tput setaf 6)\\]" white="${reset}\\[${bold}$($tput setaf 7)\\]" unset bold fi # no colors for dumb terminals if [[ "$TERM" = "dumb" ]]; then # these should also be marked readonly _PROMPT_COLOR_DARK="" _PROMPT_COLOR_LIGHT="" _PROMPT_COLOR_HIGHLIGHT="" _PROMPT_COLOR_WARNING="" _PROMPT_COLOR_RESET="" else _PROMPT_COLOR_HIGHLIGHT=$white _PROMPT_COLOR_WARNING=$red _PROMPT_COLOR_RESET=$reset # changes based on UID and roles if /cathedral/userbin/hostlist hasrole battery 2>/dev/null; then _PROMPT_POWER=true fi if (( UID == 0 )); then _PROMPT_COLOR_DARK=$red _PROMPT_COLOR_LIGHT=$light_red _PROMPT_COLOR_WARNING=$yellow else _PROMPT_COLOR_DARK=$green _PROMPT_COLOR_LIGHT=$light_green if $sourced; then if ( source /cathedral/src/lib/hostlist 2>/dev/null && hl_hasrole xorg 2>/dev/null && ( hl_hasrole system 2>/dev/null || hl_hasrole roadwarrior 2>/dev/null ) ) then _PROMPT_COLOR_DARK=$dark_gray _PROMPT_COLOR_LIGHT=$light_gray fi fi fi local OVERRIDE="${HOME}/.bash_prompt-colors" if [[ -f "$OVERRIDE" ]] && [[ -r "$OVERRIDE" ]]; then source "$OVERRIDE" fi fi # set the PS2 prompt here - easily overridable export PS2="${_PROMPT_COLOR_DARK}>${_PROMPT_COLOR_RESET} " # set PS3 (used with select) export PS3="${_PROMPT_COLOR_DARK}#?${_PROMPT_COLOR_RESET} " # set PS4 (used with tracing output, set -x) # ${0} and ${LINENO} are single-quoted, so they'll be evaluated when they're displayed instead of now export PS4="+"'${0}'":"'${LINENO}'" ${_PROMPT_COLOR_DARK}->${_PROMPT_COLOR_RESET} " # check if /proc/loadavg is readable [[ -f "/proc/loadavg" ]] && [[ -r "/proc/loadavg" ]] && _PROMPT_LOADAVG=true || _PROMPT_LOADAVG=false # check if uptime is available type uptime &>/dev/null && _PROMPT_UPTIME=true || _PROMPT_UPTIME=false # check if git is available type git &>/dev/null && _PROMPT_GIT=true || _PROMPT_GIT=false } ## RESTORE TRAPS (used by prompt command) function _prompt_command_cleanup { if $_PROMPT_DEBUGTRAP; then # clean the titles unset _PROMPT_BASH_COMMAND _prompt_title_xterm _prompt_title_screen # reset timer unset _PROMPT_SECONDS fi # traps # if [[ -n "${_PROMPT_SAVED_INTTRAP:-}" ]]; then # eval "$_PROMPT_SAVED_INTTRAP" # else # trap - INT # fi # if [[ -n "${_PROMPT_SAVED_DEBUGTRAP:-}" ]]; then # eval "$_PROMPT_SAVED_DEBUGTRAP" # fi } ## PROMPT COMMAND ## THIS RUNS FOR EVERY PROMPT - DON'T MAKE THIS HEAVIER THAN IT ALREADY IS function _prompt_command { # grab status first _PROMPT_CODE=$? # disable debug trap if $_PROMPT_DEBUGTRAP; then # _PROMPT_SAVED_INTTRAP=$(trap -p INT) # _PROMPT_SAVED_DEBUGTRAP=$(trap -p DEBUG) # trap '_prompt_command_cleanup; return' INT trap - DEBUG fi # run any preexisting prompt command if [[ -n "$_PROMPT_COMMAND_OLD" ]]; then eval "$_PROMPT_COMMAND_OLD" fi # get the last history number, and the associated command local history_number history_number=$(history 1) history_number=${history_number%% *} # if the exit status was 127, the command was not found. Let's remove it from history if [[ -n "${history_number:-}" ]]; then if (( _PROMPT_CODE == 127 )) && { [[ -z "${_PROMPT_HISTORYPOS:-}" ]] || (( ${_PROMPT_HISTORYPOS:-$history_number} < history_number )); }; then history -d "$history_number" else _PROMPT_HISTORYPOS=$history_number fi fi # if you changed the prompt yourself, we don't want to override it if [[ -n "${_PROMPT_PS1:-}" ]] && [[ -n "${PS1:-}" ]] && [[ "$_PROMPT_PS1" != "$PS1" ]]; then _prompt_command_cleanup return fi # verify the debug trap if $_PROMPT_DEBUGTRAP; then if [[ "$PROMPT_COMMAND" != "$_PROMPT_COMMAND" ]] #|| # [[ -n "${_PROMPT_SECONDS:-}" ]] && [[ "$_PROMPT_SECONDS" = *[!-0-9]* ]] || # [[ -z "${SECONDS:-}" ]] || [[ "$SECONDS" = *[!-0-9]* ]] || # [[ "$_PROMPT_SAVED_DEBUGTRAP" != "$_PROMPT_DEBUG_COMMAND" ]] then _prompt_warning 'Debug trap disabled!' _PROMPT_DEBUGTRAP=false _PROMPT_TIMER=false else _prompt_timer_stop fi fi # if we spent less than the time limit, unset the timer and the prompt wont include it if $_PROMPT_TIMER && (( _PROMPT_SECONDS < _PROMPT_TIMERLIMIT )); then unset _PROMPT_SECONDS fi # the exit status will not function if we're running more than this prompt command if $_PROMPT_EXITSTATUS; then if [[ "$PROMPT_COMMAND" != "$_PROMPT_COMMAND" ]]; then _prompt_warning 'Exit status display disabled!' _PROMPT_EXITSTATUS=false unset _PROMPT_CODE fi else unset _PROMPT_CODE fi # various stuff for the prompt local load _PROMPT_JUNK if $_PROMPT_LOADAVG; then IFS=" " read -r load _PROMPT_JUNK /dev/null | sed -e 's/.*load averages*: \(.*\...\),* \(.*\...\),* \(.*\...\).*/\1/' -e 's/ //g') fi # powah! local acpi if $_PROMPT_POWER; then local ACstat="" local charge="" acpi="$(acpi 2>/dev/null)" if [[ -n "$acpi" ]]; then case "${acpi}" in *Charging*) ACstat="^" ;; *Discharging*) ACstat="v" ;; *Full*) ACstat="=" ;; *Unknown*) # draining to avoid trickle charging ACstat="=" ;; esac charge="${acpi%\%*}%" charge="${charge##*\ }" fi fi # git support local git if $_PROMPT_GIT; then git=$(git branch 2>/dev/null | grep '^\*' | head -n 1) if [[ -n "${git}" ]]; then git=${git:2} fi fi # show background jobs local _PROMPT_JOBS_RUNNING local _PROMPT_JOBS_STOPPED read -r _PROMPT_JOBS_RUNNING < <(jobs -pr | wc -l) read -r _PROMPT_JOBS_STOPPED < <(jobs -ps | wc -l) if (( _PROMPT_JOBS_RUNNING == 0 )) && (( _PROMPT_JOBS_STOPPED == 0 )); then unset _PROMPT_JOBS_RUNNING unset _PROMPT_JOBS_STOPPED fi # unset _PROMPT_PS1 - this avoids a timing issue where the user presses ctrl+c in between the setting of _PROMPT_PS1 and PS1, breaking the prompt unset _PROMPT_PS1 # username local user if $_PROMPT_DOMAIN || [[ -z "${USER:-}" ]]; then user="\\u" else user=${USER%%@*} fi # make prompt - to make formatting somewhat readable, this is on several lines. DO NOT INDENT PS1="\ ${_PROMPT_COLOR_DARK},-${_PROMPT_LEFT}\ ${_PROMPT_COLOR_LIGHT}${user}${_PROMPT_COLOR_DARK}@\ ${_PROMPT_COLOR_LIGHT}\\h${_PROMPT_COLOR_DARK}\ ${_PROMPT_RIGHT}-\ ${_PROMPT_CODE:+${_PROMPT_COLOR_DARK}${_PROMPT_LEFT}${_PROMPT_COLOR_HIGHLIGHT}${_PROMPT_CODE}${_PROMPT_COLOR_DARK}${_PROMPT_RIGHT}-}\ ${load:+${_PROMPT_COLOR_DARK}${_PROMPT_LEFT}${_PROMPT_COLOR_HIGHLIGHT}${load}${_PROMPT_COLOR_DARK}${_PROMPT_RIGHT}-}\ ${acpi:+${_PROMPT_COLOR_DARK}${_PROMPT_LEFT}${_PROMPT_COLOR_HIGHLIGHT}${ACstat}${charge}${_PROMPT_COLOR_DARK}${_PROMPT_RIGHT}-}\ ${_PROMPT_COLOR_DARK}${_PROMPT_LEFT}${_PROMPT_COLOR_HIGHLIGHT}$(date +"$_PROMPT_DATEFORMAT")${_PROMPT_COLOR_DARK}${_PROMPT_RIGHT}\ ${_PROMPT_JOBS_RUNNING:+${_PROMPT_COLOR_DARK} - ${_PROMPT_COLOR_LIGHT}${_PROMPT_JOBS_RUNNING}${_PROMPT_COLOR_DARK}|${_PROMPT_COLOR_LIGHT}${_PROMPT_JOBS_STOPPED}${_PROMPT_COLOR_DARK}}\ ${_PROMPT_SECONDS:+${_PROMPT_COLOR_DARK} - ${_PROMPT_COLOR_LIGHT}$(_prompt_seconds2days ${_PROMPT_SECONDS})}\ ${_PROMPT_COLOR_RESET}\\n\ ${_PROMPT_COLOR_DARK}|-${_PROMPT_LEFT}${_PROMPT_COLOR_LIGHT}$(pwd)${_PROMPT_COLOR_DARK}${_PROMPT_RIGHT}\ ${VIRTUAL_ENV:+${_PROMPT_COLOR_DARK} - ${_PROMPT_COLOR_LIGHT}python:${VIRTUAL_ENV##*/}}\ ${git:+${_PROMPT_COLOR_DARK} - ${_PROMPT_COLOR_LIGHT}git:${git}}\ ${_PROMPT_COLOR_RESET}\\n\ ${_PROMPT_COLOR_DARK}\\\`-${_PROMPT_COLOR_HIGHLIGHT}\\\$${_PROMPT_COLOR_RESET} \ " # _PROMPT_PS1 is used to check if the prompt has changed the next time we run _PROMPT_PS1="${PS1}" # cleanup _prompt_command_cleanup # reset debug trap "$_PROMPT_DEBUG_COMMAND" DEBUG } ## RUN SETUP _prompt_setup unset _prompt_setup # TIMER AND PROMPT COMMAND declare -ft _prompt_command_cleanup declare -ft _prompt_command _PROMPT_COMMAND_OLD="${PROMPT_COMMAND:-}" PROMPT_COMMAND='_prompt_command' _PROMPT_COMMAND=$PROMPT_COMMAND if $_PROMPT_DEBUGTRAP; then # workaround for stupid bug where using a debug command overwrites $_ _PROMPT_DEBUG_COMMAND='_PROMPT_ARG=$_; _prompt_debug_command; : "$_PROMPT_ARG"' trap "$_PROMPT_DEBUG_COMMAND" DEBUG # _PROMPT_DEBUG_COMMAND="$(trap -p DEBUG)" fi # vim: tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab