是否有一种有效的方法在没有两次调用的情况下在bash提示符中着色文本?

时间:2013-01-31 22:22:37

标签: bash prompt

在没有两次调用的情况下,在bash提示符中为文本着色的有效方法是什么?

关于定制PS1的地方有很多资源。这里的关键点是:

  • 可以调用自定义函数生成文本,从而生成自定义文本
  • 这样的功能可以发出自定义颜色代码
  • 需要标记非打印文本(如颜色代码)以使自动换行工作
  • 自定义功能无法进行标记。

我可能在最后一点上错了,如果有办法,那就完全可以解决这个问题。

这是一个简化的,有点人为的例子,如果有意义的话,拓扑上与我正在修补的那个相似。我有一个外部命令(称之为generate_text),它在stdout上产生“OK”或一些单字消息。它也可能一无所有。这三个状态应该在提示中显示:如果它什么都不发出,请保持提示完全一样(user@hostname:path$);如果它发出OK,在提示前放一个绿色的“OK”;如果有的话,把那个文字换成红色。

我目前的解决方案是拥有一个调用generate_text的自定义函数,并根据它发出文本或发出颜色代码,然后调用该函数两次:

generate_prompt()
{
    txt=`generate_text`
    [ -z "$txt" ] && exit # The empty case. Produce nothing.
    $1 && echo "##$msg## " || case $msg in
        'OK') echo -e '\e[1;32m'; ;; # Green for OK
        *) echo -e "\e[1;31m"; ;; # Red for anything else
    esac
}
PS1='\[$(generate_prompt false)\]$(generate_prompt true)\[\e[0m\]'$PS1

这意味着我必须调用generate_text两次并假设它们将返回相同的字符串(通常情况就是这样,但理论上可能在两次调用之间状态可能会发生变化)。这让我觉得有些浪费。有没有一种方便的方法可以在同一个函数中发出非打印颜色代码和一段文本?

2 个答案:

答案 0 :(得分:3)

您可以使用PROMPT_COMMAND计算所有必需值,然后在提示中使用它们:

generate_prompt() {
  color="" message=""
  txt=$(generate_text)
  [[ -z $txt ]] && return

  message="##$txt##"
  [[ $txt == OK ]] && color=$'\e[1;32m' || color=$'\e[1;31m'
}
PROMPT_COMMAND=generate_prompt
PS1='\[$color\]$message\[\e[0m\]'$PS1

请注意,PROMPT_COMMAND有时已配置为设置xterm标题,在这种情况下,您可以添加它。

答案 1 :(得分:3)

这是没有记录的,所以可能不是一个好的解决方案,但似乎有效。它使用bash提示的内部知识,指示\ 001和\ 002字符之间的文本不计入提示字符串中的可见字符数。

generate_prompt()
{
    local txt=$(generate_text)
    case "$txt" in
        '') ;;
        OK) echo -e "\001\e[1;32m\002 $txt \001\e[0m\002"; ;; # Green for OK
         *) echo -e "\001\e[1;31m\002 $txt \001\e[0m\002"; ;; # Red for anything else
    esac
}
PS1='$(generate_prompt)'$PS1

扩展提示字符串的bash源包含以下用于制定上述解决方案的注释:

/* Current implementation:
    \001 (^A) start non-visible characters
    \002 (^B) end non-visible characters
   all characters except \001 and \002 (following a \001) are copied to
   the returned string; all characters except those between \001 and
   \002 are assumed to be `visible'. */