Bash prompt with the last exit code
- 2024-10-29 08:35:00
- admin 原创
- 53
问题描述:
I've been trying to customize my Bash prompt so that it will look like
[feralin@localhost ~]$ _
with colors. I managed to get constant colors (the same colors every time I see the prompt), but I want the username ('feralin') to appear red, instead of green, if the last command had a nonzero exit status. I came up with:
e[1;33m[$(if [[ $? == 0 ]]; then echo "e[0;31m"; else echo "e[0;32m"; fi)/ue[m@e[1;34mh e[0;35mWe[1;33m]$ e[m
However, from my observations, the $(if ...; fi)
seems to be evaluated once, when the .bashrc
is run, and the result is substituted forever after. This makes the name always green, even if the last exit code is nonzero (as in, echo $?
). Is this what is happening? Or is it simply something else wrong with my prompt? Long question short, how do I get my prompt to use the last exit code?
解决方案 1:
As you are starting to border on a complex PS1, you might consider using PROMPT_COMMAND
. With this, you set it to a function, and it will be run after each command to generate the prompt.
You could try the following in your ~/.bashrc
file:
PROMPT_COMMAND=__prompt_command # Function to generate PS1 after CMDs
__prompt_command() {
local EXIT="$?" # This needs to be first
PS1=""
local RCol='[e[0m]'
local Red='[e[0;31m]'
local Gre='[e[0;32m]'
local BYel='[e[1;33m]'
local BBlu='[e[1;34m]'
local Pur='[e[0;35m]'
if [ $EXIT != 0 ]; then
PS1+="${Red}/u${RCol}" # Add red if exit code non 0
else
PS1+="${Gre}/u${RCol}"
fi
PS1+="${RCol}@${BBlu}h ${Pur}W${BYel}$ ${RCol}"
}
This should do what it sounds like you want. Take a look a my bashrc's sub file if you want to see all the things I do with my __prompt_command
function.
解决方案 2:
If you don't want to use the prompt command there are two things you need to take into account:
getting the value of
$?
before anything else. Otherwise it'll be overridden.escaping all the
$
's in the PS1 (so it's not evaluated when you assign it)
Working example using a variable
PS1="$(VALU="$?" ; echo $VALU ; date ; if [ $VALU == 0 ]; then echo zero; else echo nonzero; fi) "
Working example without a variable
Here the if needs to be the first thing, before any command that would override the $?
.
PS1="$(if [ $? == 0 ]; then echo zero; else echo nonzero; fi) "
Notice how the $()
is escaped so it's not executed right away, but each time PS1 is used. Also all the uses of $?
.
解决方案 3:
Compact solution:
PS1='... $(code=${?##0};echo ${code:+[error: ${code}]})'
This approach does not require PROMPT_COMMAND
(apparently this can be slower sometimes) and prints [error: <code>]
if the exit code is non-zero, and nothing if it's zero:
... > false
... [error: 1]> true
... >
Change the [error: ${code}]
part depending on your liking, with ${code}
being the non-zero code to print.
Note the use of '
to ensure the inline $()
shell gets executed when PS1 is evaluated later, not when the shell is started.
As bonus, you can make it colorful in red by adding e[01;31m
in front and e[00m
after to reset:
PS1='... e[01;31m$(code=${?##0};echo ${code:+[error: ${code}]})e[00m'
--
How it works:
it uses bash parameter substitution
first, the
${?##0}
will read the exit code$?
of the previous commandthe
##
will remove any0
pattern from the beginning, effectively making a0
result an empty var (thanks @blaskovicz for the trick)we assign this to a temporary
code
variable as we need to do another substitution, and they can't be nestedthe
${code:+REPLACEMENT}
will print theREPLACEMENT
part only if the variablecode
is set (non-empty)this way we can add some text and brackets around it, and reference the variable again inline:
[error: ${code}]
解决方案 4:
I wanted to keep default Debian colors, print the exact code, and only print it on failure:
# Show exit status on failure.
PROMPT_COMMAND=__prompt_command
__prompt_command() {
local curr_exit="$?"
local BRed='[e[0;91m]'
local RCol='[e[0m]'
PS1='${debian_chroot:+($debian_chroot)}[[01;32m]/u@h[[00m]:[[01;34m]w[[00m]$ '
if [ "$curr_exit" != 0 ]; then
PS1="[${BRed}$curr_exit${RCol}]$PS1"
fi
}
解决方案 5:
The following provides a leading green check mark when the exit code is zero and a red cross in all other cases. The remainder is a standard colorized prompt. The printf
statements can be modified to present the two states that were originally requested.
PS1='$(if [ $? -eq 0 ]; then printf "[01;32m""xE2x9Cx93"; else printf "[01;31m""xE2x9Cx95"; fi) [e[00;32m]/u@h[e[00;30m]:[e[01;33m]w[e[01;37m]$ '
解决方案 6:
Why didn't I think about that myself? I found this very interesting and added this feature to my 'info-bar' project. Eyes will turn red if the last command failed.
#!/bin/bash
eyes=(O o ∘ ◦ ⍤ ⍥) en=${#eyes[@]} mouth='_'
face () { # gen random face
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
if [[ $1 ]]; then printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}"
else printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
fi
}
info () { error=$?
[[ -d .git ]] && { # If in git project folder add git status to info bar output
git_clr=('GIT' $(git -c color.ui=always status -sb)) # Colored output 4 info
git_tst=('GIT' $(git status -sb)) # Simple output 4 test
}
printf -v line "%${COLUMNS}s" # Set border length
date=$(printf "%(%a %d %b %T)T") # Date & time 4 test
test=" O_o $PWD ${git_tst[*]} $date o_O " # Test string
step=$[$COLUMNS-${#test}]; [[ $step -lt 0 ]] && step=0 # Count spaces
line="$GRN${line// /-}$DEF
" # Create lines
home="$BLD$BLU$PWD$DEF" # Home dir info
date="$DIM$date$DEF" # Colored date & time
#------+-----+-------+--------+-------------+-----+-------+--------+
# Line | O_o |homedir| Spaces | Git status | Date| o_O | Line |
#------+-----+-------+--------+-------------+-----+-------+--------+
printf "$line $(face) $home %${step}s ${git_clr[*]} $date $(face)
$line" # Final info string
}
PS1='${debian_chroot:+($debian_chroot)}
$(info)
$ '
case "$TERM" in xterm*|rxvt*)
PS1="[e]0;${debian_chroot:+($debian_chroot)} $(face 1) wa]$PS1";;
esac
解决方案 7:
In bash, it's possible to show return codes only on failure, i.e. non-zero exit of the previous command, and without polluting exit status variables state, as PROMPT_COMMAND
& $(...)
subshells do, with
PS1="${?#0} $ "
In PS1 context,
${?#0}
defers evaluation of?
, last exit status, and removes leading 0, which only occurs for 0 in 0-255 possible exit statuses.$
appears as#
when running as root, and$
as a normal user
$ man bash
BASH(1) ...
PARAMETERS ...
Special Parameters ...
? Expands to the exit status of the most recently executed foreground pipeline.
It's helpful to avoid PROMPT_COMMAND
and $(...)
subshells, because they pollute the last exit code with their own exit code. Kludges to work around this can include returning previous exit codes explicitly, but it's easy to miss, tricky to get right, not robust.
styling
Wrap this inclusion in ANSI color codes, and it can stand out too.
declare -A _c
_c[red_ul]="[0;31;4m"
_c[nc]="[0m"
PS1="${_c[red_ul]}${?##0}${_c[nc]} $ "
Note: Escape the $
prompt indicator, so it shows as #
in a root shell ("EUID = 0").
testing it out
_ret(){ return ${1:-0};}
$ _ret
$ _ret 1
1 $
1 $ _ret 2
2 $ :
$
bash's PIPESTATUS
Use echo "${PIPESTATUS[@]}"
or declare -p PIPESTATUS
to preview this value.
You can include it in PS1
as "${PIPESTATUS[@]}"
, but recommend you color it in a muted way. No fancy tricks for hiding the last return, 1-command exit 0.
You can test with $ _ret 5 | _ret 6
followed by $ echo "${PIPESTATUS[@]}"
before including it in a temporary, then permanent PS1.
解决方案 8:
Improved demure answer:
I think this is important because the exit status is not always 0 or 1.
if [ $EXIT != 0 ]; then
PS1+="${Red}${EXIT}:/u${RCol}" # Add red if exit code != 0
else
PS1+="${Gre}${EXIT}:/u${RCol}" # Also displays exit status
fi
解决方案 9:
Bash
function my_prompt {
local retval=$?
local field1='/u@h'
local field2='w'
local field3='$([ $SHLVL -gt 1 ] && echo shlvl:$SHLVL)$([ j -gt 0 ] && echo jobs:j)'"$([ ${retval} -ne 0 ] && echo exit:$retval)"
local field4='$'
PS1=$'
'"e[0;35m${field1}e[m e[0;34m${field2}e[me[0;31m${field3}e[m"$'
'"[e[0;36m]${field4}[e[m] "
}
PROMPT_COMMAND="my_prompt; ${PROMPT_COMMAND}"
Zsh
PROMPT=$'
''%F{magenta}%n@%m%f %F{blue}%~%f%F{red}%(2L. shlvl:%L.)%(1j. jobs:%j.)%(?.. exit:%?)%f'$'
''%F{cyan}%(!.#.$)%f '
Images of prompt
解决方案 10:
To preserve the original prompt format (not just colors),
you could append following to the end of file ~/.bashrc
:
PS1_ORIG=$PS1 # original primary prompt value
PROMPT_COMMAND=__update_prompt # Function to be re-evaluated after each command is executed
__update_prompt() {
local PREVIOUS_EXIT_CODE="$?"
if [ $PREVIOUS_EXIT_CODE != 0 ]; then
local RedCol='[e[0;31m]'
local ResetCol='[e[0m]'
local replacement="${RedCol}/u${ResetCol}"
# Replace username color
PS1=${PS1_ORIG//]\/u/]$replacement}
## Alternative: keep same colors, append exit code
#PS1="$PS1_ORIG[${RedCol}error=$PREVIOUS_EXIT_CODE${ResetCol}]$ "
else
PS1=$PS1_ORIG
fi
}
See also the comment about the alternative approach that preserves username color and just appends an error code in red to the end of the original prompt format.
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件