带有最后退出代码的Bash提示


70

因此,我一直在尝试通过bash提示进行自定义,以使其看起来像

[feralin@localhost ~]$ _

颜色。我设法获得了恒定的颜色(每次看到提示时,颜色都是相同的),但是如果最后一个命令的退出状态为非零,我希望用户名(“ feralin”)显示为红色,而不是绿色。我想出了:

\e[1;33m[$(if [[ $? == 0  ]]; then echo "\e[0;31m"; else echo "\e[0;32m"; fi)\u\e[m@\e[1;34m\h \e[0;35m\W\e[1;33m]$ \e[m

但是,从我的观察来看,运行$(if ...; fi)时似乎只对一次求值.bashrc,然后永远替换结果。即使最后一个退出代码为非零(如),这也使名称始终为绿色echo $?。这是怎么回事吗?还是提示出现其他问题?长话短说,如何提示我使用最后一个退出代码?


3
我也从来没有成功过。但是,我所做的是将$ {?#0}放入提示中,当且仅当它为非零时,它才会显示数字退出状态。
Wes Hardaker

3
它按原样工作。您只是反转了绿色和红色。
n。代词

3
Prompt.gem的无耻插件,提供了可扩展的提示,其中包括退出代码和上一个命令的持续时间。
dimo414 '16

2
回复:“在.bashrc运行时进行一次评估”-表示分配给时,您会在其周围加上错误的引号PS1。需要是单身,而不是双身。如果那没有帮助,那么在$?打印提示之前,您将被正在运行的其他内容所重置;set -x将使向下跟踪此类命令。
查尔斯·达菲,2016年

@CharlesDuffy感谢您的提示!我会记住的。
feralin

Answers:


118

当您开始在复杂的PS1上边框时,可以考虑使用PROMPT_COMMAND
这样,您将其设置为一个函数,它将在每个命令之后运行以生成提示。

您可以尝试以下方法 ~/.bashrc

PROMPT_COMMAND=__prompt_command # Func to gen 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}"
}

这应该按照您希望的方式进行。如果要查看我对函数执行的所有操作,请查看我的bashrc的子文件__prompt_command


有趣。我不了解PROMPT_COMMAND。我现在尝试。
feralin

好吧,它有效!我只是稍作更改,在“ $”之前包括了分度数。除此之外,它是完美的。谢谢!
Feralin

2
我建议使用一个变量名,其中至少包含一个小写字符EXIT,作为前向兼容性实践。请参阅pubs.opengroup.org/onlinepubs/009695399/basedefs/…,第四段-全大写字母名称空间用于对系统或外壳有意义的变量;坚持使用小写字母名称可以防止仅尝试分配shell变量时覆盖环境变量或shell-builtin变量。当然,通过本地声明覆盖是临时的,这使得它比平时少了一个问题,但仍然不理想。
查尔斯·达菲

5
在Mac上,您需要PROMPT_COMMAND="__prompt_command; ${PROMPT_COMMAND}"启用在当前工作目录中打开新标签页/窗口的功能。PROMPT_COMMAND=update_terminal_cwd默认情况下,它将记录cwd。
艾萨克·特纳

3
Bash变量PIPESTATUS提供有关最后执行的命令或管道的更多信息。${PIPESTATUS[@]}将是一个零字符串,例如0 0 0所有命令都已正确执行,否则它将具有非零字符串,例如0 127 1。可以使用来检查是否成功if $(echo ${PIPESTATUS[@]} | grep -qEe '^0( 0)*$'); then echo "good"; else echo "bad"; fi
Nik O'Lai

18

如果您不想使用提示命令,则需要考虑两件事:

  1. 得到$的价值?首先,否则它将被覆盖
  2. 转义PS1中的所有$(因此在分配它时不会对其进行评估)

使用变量的工作示例

PS1="\$(VALU="\$?" ; echo \$VALU ; date ; if [ \$VALU == 0 ]; then echo zero; else echo nonzero; fi) " 

没有变量的工作示例

在这里,如果要先执行if,那么它将覆盖所有命令$?

PS1="\$(if [ \$? == 0 ]; then echo zero; else echo nonzero; fi) "

请注意如何\$()转义,因此不会立即执行,而是在每次使用PS1时执行。还有所有的用途\$?


3
太棒了!自1995年以来我就一直是肮脏的老混蛋,对我来说从未在PS1中使用命令替换。谢谢。
布鲁诺·布罗诺斯基

3
尽管我更喜欢简单的三元表示法:export PS1="\$([ \$? == 0 ] && echo ✅ || echo ⚠️ ) \h:\W \u\n\$ "
Bruno Bronosky

这很酷,我目前正在编辑.bashrc文件以将其合并。小巧的尼特:用“ -eq”代替“ ==“(和用“ =”代替“ ==“)。
keithpjolley

使用像Python的工具时,该解决方案是特别有趣virtualenv,其前缀PS1激活时与信息变量。使用PROMPT_COMMAND将不起作用!
2017年

看来,如果您使用的\${PIPESTATUS[-1]}话,即使不是最后一个命令,您也可以获得最后一个命令的退出状态PS1
David Ongaro

7

我想保留默认的Debian颜色,打印确切的代码,仅在失败时打印:

# 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)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

    if [ "$curr_exit" != 0 ]; then
        PS1="[${BRed}$curr_exit${RCol}]$PS1"
    fi
}

1

紧凑型解决方案:

PS1='... $(code=${?##0};echo ${code:+[error: ${code}]})'

这种方法不需要PROMPT_COMMAND(显然有时会慢一些),并且[error: <code>]如果退出代码为非零,则打印,如果为零,则不打印:

... > false
... [error: 1]> true
... >

[error: ${code}]根据您的喜好更改零件,并${code}打印非零代码。

请注意,使用'来确保在$()稍后评估PS1时而不是在启动Shell时执行内联Shell。

作为奖励,您可以通过\e[01;31m在前后添加\e[00m重置来使它变成红色的彩色:

PS1='... \e[01;31m$(code=${?##0};echo ${code:+[error: ${code}]})\e[00m'

-

这个怎么运作:

  • 它使用bash参数替换
  • 首先,${?##0}会读取$?上一个命令的退出代码
  • ##将删除任何0从一开始的模式,有效地使一个0结果,一个空的VAR(感谢@blaskovicz用于特技)
  • 我们将其分配给一个临时code变量,因为我们需要进行另一次替换,并且它们不能嵌套
  • 仅在设置了变量(非空)的情况下${code:+REPLACEMENT}才会打印REPLACEMENT零件code
  • 这样,我们可以在其周围添加一些文本和方括号,然后再次内联引用该变量: [error: ${code}]

0

改进的@demure

我认为这很重要,因为并不总是将退出状态设为0或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

2
您没有引用扩展的原因吗?如果用户设置了此PROMPT_COMMAND IFS=0,则该用户将if [ != 0 ]处于扩展状态(当然退出代码为零);使得它if [ "$EXIT" != 0 ]会避免这种情况。
查尔斯·达菲,2016年

0

要保留原始提示格式(不仅是颜色),您可以将以下内容附加到~/.bashrc

PS1_ORIG=$PS1 # original primary prompt value
PROMPT_COMMAND=__update_prompt # Func 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
}

另请参阅有关保留用户名颜色并仅将红色错误代码附加到原始提示格式末尾的替代方法的注释


此解决方案某种程度上是基于Demure和Velkan的想法/解决方案(但保留了原始格式和颜色)
atsu85 '20

0

为什么我自己没想到呢?)我发现这很有趣,并将此功能添加到我的“信息栏”项目中。如果最后一条命令失败,眼睛将变成红色。

#!/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\n"                            # 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) \n$line" # Final info string    
}                                                                                                     
PS1='${debian_chroot:+($debian_chroot)}\n$(info)\n$ '                                                 
case "$TERM" in xterm*|rxvt*)                                                                         
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)} $(face 1) \w\a\]$PS1";;                            
esac 

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.