Bash提示符和在函数中输出彩色文本

50
我在我的 .bashrc 文件中有以下内容:
LIGHTGREEN="\[\033[1;32m\]"
LIGHTRED="\[\033[1;31m\]"
WHITE="\[\033[0;37m\]"
RESET="\[\033[0;00m\]"

function error_test {
    if [[ $? = "0" ]]; then
        echo -e "$LIGHTGREEN"
    else
        echo -e "$LIGHTRED"
    fi
}

PS1="\u\$(error_test)@\w$RESET \$ "

看起来这样可以准确地输出shell:

username\[\]@~/

颜色代码周围的转义符[和]在我的提示符中显示出来。如果我从颜色的周围删除转义码,它可以工作,但是bash行包装失败了。

请注意,如果我执行PS1="LIGHTGREEN - whatever - $RESET",它可以工作,并且[和]没有被转义。然而,我希望在函数内执行此操作,这似乎是问题所在。

我找不到任何关于此的良好文档。 man echo甚至没有列出-e选项。Bash似乎有很多未记录的知识。


“help echo” 列举了几个特定于 bash 的选项。 - Ignacio Vazquez-Abrams
1
这是因为手册页是针对外部变量的。你有/bin/echo,它在man 1 echo中有记录,还有Bash的内置函数echo,它在help echo中有记录。更多信息请参见help helpman bash - tripleee
6
欢迎来到2017年!对于未来的旅行者,最简单的答案是:将 \[ 替换为 \001,将 \] 替换为 \002。具体可参考 https://dev59.com/_Gw15IYBdhLWcg3wd7lj#43462720。 - Chris Nolet
6个回答

61

我在寻找如何通过从bash函数中转义 \[ \] 来设置bash颜色时,发现了这个话题。

实际上,有一个解决方案。Bash允许每次呈现提示时生成PS1提示。

set_bash_prompt(){
    PS1="\u@\h $(call_your_function) $>"
}

PROMPT_COMMAND=set_bash_prompt

这样做,每次显示提示符时都会解释PS1,因此它将调用函数并正确呈现所有转义序列,包括重要的\[ \],以计算提示符的长度(例如,使命令历史记录正常工作)。

希望这能帮助到某些人,因为我花了半天时间解决这个问题。


1
这就是解决方案。请参考@Ignacio Vazquez-Abrams之前的答案,了解它为什么有效。 - joemaller
5
如果您在调用call_your_function时使用颜色,就需要使用PROMPT_COMMAND。否则,提示符的长度将不正确,并且命令历史记录会显示不正常。 - Lætitia
1
@Tonin 不,你可以在“PS1”命令中正确使用颜色。示例 - l0b0
2
这个很好用,谢谢!不幸的是,它会破坏Terminal.app打开以活动选项卡路径为根的新选项卡的能力。可以通过以下方式恢复此功能:PROMPT_COMMAND="set_bash_prompt; $PROMPT_COMMAND"。有关更多详细信息,请参见http://superuser.com/a/623305/75328。 - Chad von Nau
这太棒了!帮助我解决了一个问题,我试图在每个命令输出后插入一个换行符,但如果我的路径很长,它会破坏提示符。只需在函数末尾添加一个额外的“echo”,现在它就像应该一样工作了!谢谢! - drmrgd
显示剩余4条评论

42

使用\001代替\[,使用\002代替\],同时要注意使用PROMPT_COMMAND的后果,因为这种方法会在每次重置提示符(这也可能正是你想要的)。有关在bash提示符中使用颜色的解决方案在这里说明:

\[\]仅在分配PS1时才是特殊字符,如果你将它们打印在显示提示符时运行的函数内部,则无法起作用。在这种情况下,需要使用字节\001\002

还有这个答案也指向同一方向:

专属于bash的\[\]实际上被翻译成\001\002

如建议的那样通过PROMPT_COMMAND调用的函数内设置PS1每次都会重置PS1,从而不允许其他脚本轻松修改你的提示符(例如Python虚拟模拟环境activate.sh):

$ echo $PS1
<your PS1>
$ PS1="(TEST)$PS1"
$ echo $PS1
<(TEST) is not prepended to PS1 if you are using PROMPT_COMMAND as it is reset>

5
天啊,我简直无法相信这个问题已经六年了,而恰好你在三天前发布了唯一有效的解决方案!非常感谢。这需要更多的赞 :) - Chris Nolet
3
这个解决方案对我有效,结合使用 printf 而不是 echo - LostMyGlasses
哦,我的天啊,你真是个英雄。这个完美地运行了,而且立即就解决了问题,同时还帮助我真正理解了出了什么问题。非常感谢你。 - Mark
一个好问题是 - 为什么每个人(PS1教程)都说要用\[转义颜色,而不是立即教更多功能的\001。编辑:我错了,当然Arch Linux教程立刻就提到了它!:D - Yurkee
很高兴我找到了这个方法,并且在一个函数中使用printf "%b"echo -e使其正常工作。谢谢! - undefined

8

\[\]必须直接在$PS*中使用,而不仅仅是通过echo输出它们。

LIGHTGREEN="\033[1;32m"
LIGHTRED="\033[1;31m"
WHITE="\033[0;37m"
RESET="\033[0;00m"

function error_test {
    if [[ $? = "0" ]]; then
        echo -e "$LIGHTGREEN"
    else
        echo -e "$LIGHTRED"
    fi
}

PS1="\u\[\$(error_test)\]@\w\[$RESET\] \$ "

3
如果一个函数输出的不仅仅是颜色,比如说它输出 echo -e "$LIGHTGREEN some stuff $RESET" ,那该怎么办呢?这个解决方案把函数调用用 \[\] 包裹起来,但似乎也会有同样的问题。 - Andy Ray
这就是为什么你不能那样做的原因。 - Ignacio Vazquez-Abrams
你是说在bash中无法创建一个同时输出颜色和文本的函数? - Andy Ray
一定有其他方法 - 我有一些相当复杂的逻辑,我想用它来构建我的bash提示符,但是太长了无法放在一个PS1=行中,所以我将其放在一个函数中。我甚至不知道它是否可以真实地放在一行中。我想用多种颜色对输出进行着色。 - Andy Ray
我的 .bashrc 文件使用了函数和多种颜色来设置 $PS1。它完美地运行了(商标)。 - l0b0
当然有办法:echo -e "\001${red}\002" - FelipeC

0
这是我的PS1代码中彩色退出代码部分:
color_enabled() {
    local -i colors=$(tput colors 2>/dev/null)
    [[ $? -eq 0 ]] && [[ $colors -gt 2 ]]
}

BOLD_FORMAT="${BOLD_FORMAT-$(color_enabled && tput bold)}"
ERROR_FORMAT="${ERROR_FORMAT-$(color_enabled && tput setaf 1)}"
RESET_FORMAT="${RESET_FORMAT-$(color_enabled && tput sgr0)}"

# Exit code
PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s $BOLD_FORMAT $ERROR_FORMAT $exit_code $RESET_FORMAT " ")'

屏幕截图(其中一个Subversion存储库路径已匿名): 彩色编码输出


1
该死的Stackoverflow评论。让我们再试一次:我尝试了你的方法,似乎和我的问题一样,即bash行换行符会中断。在行末输入文本会使文本换行到同一行。我做错了什么吗?: https://gist.github.com/1071081 - Andy Ray
此外,如果我尝试将退出代码作为字符串中的函数,并执行 $(${exit_code}),代码会保持不变。这是因为像 tput 这样的其他命令正在重置它吗? - Andy Ray
@AndyRay 这个答案和你的Bash Prompt Builder似乎没有解决换行和命令历史记录问题。@thedk的回答是唯一对我起作用的一个。 - Lætitia
@Tonin 你能演示一下它未被解决的情况吗?如果我的.bashrc文件有问题,我很想知道。 - l0b0
@l0b0,使用您提供的代码片段在我的.bashrc中,当我使用ctrl-r返回历史命令时,如果退出码为>0,则提示符会被右侧截断(即字符计数不正确)。 - Lætitia
显示剩余5条评论

0

这将很好地工作。

LIGHTGREEN="\e[32m"
LIGHTRED="\e[31m"
RESET="\e[0m"

error_test () {
    if [[ $? = "0" ]]; then
        echo -e "$LIGHTGREEN"
    else
        echo -e "$LIGHTRED"
    fi
}
export PS1=$(printf "$(error_test) $(whoami)@${RESET}$(pwd) ")

0

我知道这是一个老话题,但我刚刚用函数使它工作了。诀窍是将函数的打印和非打印部分分开,以便您可以使用 [ ] 正确地括起非打印部分。通常我喜欢我的 ERROR.. 行是单独的(那时这不是问题),但如果一切都在一行中,这也可以正确地工作。

请注意,我从每个子 shell 返回先前的 $? 值,因此 $? 从一个 shell 传播到下一个 shell。

PS1="\n\
\[\`
  cja_prv_retval=\$?;
  if [ \$cja_prv_retval != 0 ];
     then echo -ne \$E_ERROR;
  fi
  exit \$cja_prv_retval
\`\]\
\`
  cja_prv_retval=\$?;
  if [ \$cja_prv_retval != 0 ];
     then echo -ne \"ERROR: RETURN CODE \$cja_prv_retval\";
  fi
  exit \$cja_prv_retval
\`\
\[\`
  cja_prv_retval=\$?;
  if [ \$cja_prv_retval != 0 ];
     then echo -ne \$E_RESET;
  fi
  exit \$cja_prv_retval
\`\]\
${P_RESET}${P_GRAY}\! \t ${P_RED}\u${P_GRAY}@${P_GREEN}\h ${P_YELLOW}\w ${P_CYAN}   ══>${P_RESET} "

这将给我任一

2021 12:28:05 cja@morpheus04 ~ ══>

如果没有错误,或者
ERROR: RETURN CODE 1 2021 12:28:16 cja@morpheus04 ~ ══>

如果出现错误,一切都正确地间隔(多行历史编辑正常工作)。


你也可以查看我的完整函数,了解我是如何完成它的:http://andrewray.me/bash-prompt-builder/index.html - Andy Ray
返回代码链接技巧帮助我解决了我的提示谜题;以前,我要么可以安全地括起颜色代码进行合理的编辑,要么可以根据上一个退出代码生成不同的提示,但由于子shell使传递变量变得不可能,因此两者都无法实现。 - Tangent 128

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接