87

我一直在尝试自定义我的 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 $?)。这是正在发生的事情吗?还是我的提示有其他问题?很长的问题,我如何提示我使用最后一个退出代码?

4

8 回答 8

139

当您开始使用复杂的PS1时,您可能会考虑使用PROMPT_COMMAND. 有了这个,你将它设置为一个函数,它将在每个命令之后运行以生成提示。

您可以在~/.bashrc文件中尝试以下操作:

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}"
}

这应该做它听起来像你想要的。如果您想查看我对函数所做的所有事情,请查看我的bashrc子文件__prompt_command

于 2013-05-23T13:48:21.960 回答
22

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

  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 时。也是所有用途\$?

于 2017-05-27T07:17:40.647 回答
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
}
于 2016-09-11T16:24:17.550 回答
6

紧凑型解决方案:

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结果成为空变量(感谢@blaskovicz的技巧)
  • 我们将它分配给一个临时code变量,因为我们需要进行另一个替换,并且它们不能嵌套
  • 仅当设置了变量(非空)${code:+REPLACEMENT}时才会打印该部分REPLACEMENTcode
  • 这样我们可以在它周围添加一些文本和括号,并再次内联引用变量:[error: ${code}]
于 2020-05-11T22:06:04.253 回答
1

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

#!/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

在此处输入图像描述

于 2020-06-03T14:56:57.327 回答
1

当退出代码为零时,以下提供前导绿色复选标记,在所有其他情况下提供红色叉号。其余的是标准的彩色提示。printf可以修改语句以呈现最初请求的两种状态。

PS1='$(if [ $? -eq 0 ]; then printf "\033[01;32m""\xE2\x9C\x93"; else printf "\033[01;31m""\xE2\x9C\x95"; fi) \[\e[00;32m\]\u@\h\[\e[00;30m\]:\[\e[01;33m\]\w\[\e[01;37m\]\$ '
于 2021-05-27T04:01:13.890 回答
0

改进娴静的答案

我认为这很重要,因为退出状态并不总是 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
于 2015-04-27T21:52:17.360 回答
0

要保留原始提示格式(不仅仅是颜色),您可以将以下内容附加到文件末尾~/.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
}

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

于 2020-06-03T11:45:00.107 回答