重击:在提示中显示退出状态:


11
GREEN="\e[1;32m"
RED="\e[1;31m"
NONE="\e[m"

get_exit_status(){
   es=$?
   if [ $es -eq 0 ]
   then
       echo -e "${GREEN}${es}${NONE}"
   else
       echo -e "${RED}${es}${NONE}"
   fi
}

get_path(){
    #dummy function
    echo "PATH"
}

PROMPT_COMMAND='exitStatus=$(get_exit_status)'

以下内容为我提供了正确的exitStatus,但未扩展颜色变量:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

但是,下面的一种给我颜色,但是退出状态不会更新:

PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "

什么是正确的方法?如何解决此问题,使exitStatus和color都能正常工作?

Answers:


8

吉尔斯(Gilles)确定了您的主要问题,但我想尝试以不同的方式解释它。

Bash 仅扩展提示中的任何变量之前解释特殊提示转义。这意味着,在提示中扩展的变量中使用,即使它直接在中也不能使用。\ePS1

例如,这按预期工作,并显示红色文本:

PS1='\e[1;31m this is in red '

但这不是,它只是\e在提示中输入文字:

RED='\e[1;31m'
PS1="$RED not in red "

如果要将颜色转义$'...'符存储在变量中,则可以使用ANSI-C引号()在变量中添加文字转义符。

要做到这一点,你可以改变你的定义GREENREDNONE,因此他们的价值是实际的转义序列。

GREEN=$'\033[1;32m'
RED=$'\033[1;31m'
NONE=$'\033[m'

如果这样做,那么PS1用单引号引起的第一个应该起作用:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

但是,那么您将遇到第二个问题。

尝试运行该命令,然后按,然后按Up ArrowHome光标将不会返回到行首。

要解决此问题,请更改PS1为包括颜色转义序列\[\]在其周围,例如

PS1='\[${RED}\]\h $(get_path) $?\[${NONE}\] '

get_exit_status在这里不能正确使用,因为它的输出既包含打印(退出代码)又包含非打印字符(颜色代码),并且无法在提示中正确标记。推入会\[...\]将其标记为未完全打印,这是不正确的。您必须更改功能,使其仅打印正确的颜色代码,然后\[...\]在提示中将其括起来。


\[\1\[\2。那些对应于某些readline的RL_PROMPT_{START,END}_IGNORE东西,要求它在计算屏幕上的提示长度时忽略字节。请参阅lists.gnu.org/archive/html/bug-bash/2015-08/msg00027.html
Arthur2e5

@ Arthur2e5你的意思\]\2吗?并且您是说这就是为什么需要它的原因${exitStatus}吗?我的观点是,${exitStatus}它不包含非打印字符,因此bash应该能够正确确定在不使用\[and \]in的情况下将提示移动多少个字符\[${exitStatus}\]
Mikel

问题是它确实包含一些颜色。(ANSI Escapes)
Arthur2e5,2015年

@ Arthur2e5 Ew,我完全错过了。:)为什么要放颜色...没关系。:)
Mikel

1
“重击实际上是在PS1上调用echo,而不是echo -e” –嗯,这是错误的,或者只是遗漏了要点。Bash确实从提示符处扩展了反斜杠转义,例如\e\033(和\[/ 和/ \]\u以及和\h),它只是扩展变量之前这样做。所以PS1='\e[1;31m red'作品,red='\e[1;31m'; PS1='$red red'其实不然。
ilkkachu

3

运行时PS1='${RED}\h $(get_path) ${exitStatus}${NONE} 'PS1变量设置为${RED}\h $(get_path) ${exitStatus}${NONE},其中仅\h是提示转义序列。提示的序列被扩展(产生${RED}darkstar $(get_path) ${exitStatus}${NONE}),壳执行常见的展开诸如可变扩展。您将看到一个类似的显示提示\e[1;31mdarkstar PATH 0\e[m。沿途没有任何方法将\e序列扩展为实际的转义字符。

运行时PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "PS1变量设置为\e[1;31m\h PATH 0\e[m。变量REDexitStatusNONE在分配的时间扩展。然后提示包含三个提示转义序列(\e\h,和\e再次)。在此阶段,没有外壳变量可以扩展。

为了查看颜色,您需要颜色变量包含实际的转义字符。您可以这样操作:

RED=$'\033[1;31m'
NONE=$'\033[m'
PS1='\[${RED}\]\h \w $?\[${NONE}\] '

$'…'扩展反斜杠八进制序列和一些反斜杠字母序列,例如\n,但不包括\e。我还对您的提示做了三处更改:

  • 使用\[…\]围绕非打印序列,例如颜色改变命令。否则,由于bash无法确定提示的宽度,您的显示将最终出现乱码。
  • \w 是用于输出当前目录的内置转义序列。
  • 如果您首先没有,则无需$?在提示中显示任何复杂的内容PROMPT_COMMAND

我认为这样做的目的是使提示在成功时为绿色,在失败时为红色。
mattdm 2011年

是的,PS1是错的,但建议使用$'...'RED,并GREEN应该使用罗布麻的工作PS1
Mikel

1

尝试:

PS1='`exitStatus=$?;if [ $exitStatus -eq 0 ];then echo "\['${GREEN}'\]";else echo "\['${RED}'\]";fi;echo "\h $(get_path) ${exitStatus}${NONE}"`'

1
谢谢,此方法有效,但是有什么方法可以完成此操作而不必在提示中嵌入if语句?
dogbane'3

1

这是我一直使用的方法,它避免使用PROMPT_COMMAND

# This function is called from a subshell in $PS1,
# to provide a colourised visual indicator of the exit status of the last run command
__COLOURISE_EXIT_STATUS() {
    # uncomment the next line for exit code output after each command, useful for debugging and testing
    #printf -- "\nexit code: $1\n" >&2
    [[ 0 == "$1" || 130 == "$1" ]] && printf -- "$GREEN" || printf -- "$RED"
}

那么我$PS1的如下:

PS1='# ${debian_chroot:+($debian_chroot)}'"${GREEN}\u${YELLOW}@${DARK_YELLOW}\h${WHITE}:${LIGHT_BLUE}\w${WHITE}\n"'\[$(__COLOURISE_EXIT_STATUS $?)\]# \$'"\[${WHITE}\] "

1
尽管在这种情况下无关紧要,因为唯一的值$?可以是整数,所以您实际上应该使用整数printf '%b' "$GREEN"。另外,避免使用以bash-completion 开头__或结尾的函数名_
nyuszika7h 2014年

1

在这里 -在Ubuntu和其他Linux(Linuxen?)上,这对我有效(TM)。

进行退出代码检测的原因$PS1是,一台主机在读取$PROMPT_COMMAND.bashrc之前已设置了只读。


0

对于PROMPT_COMMAND,定义函数并使用该函数更干净:

prompt_command() {
    # ...
}
PROMPT_COMMAND=prompt_command
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.