1

无需两次调用即可在 bash 提示符中为文本着色的有效方法?

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

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

我在最后一点上可能是错的,如果有办法,那将完美地解决这个问题。

这是一个简化且有些人为的示例,如果这有意义的话,它在拓扑上与我正在修补的示例相似。我有一个外部命令(调用它generate_text),它在标准输出上产生“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 两次并假设它们将返回相同的字符串(通常是这种情况,但理论上状态可能会在两次调用之间发生变化)。这让我觉得有点浪费。有没有一种方便的方法可以在同一个函数中同时发出非打印颜色代码和一段文本?

4

2 回答 2

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'. */ 
于 2013-01-31T23:39:55.280 回答
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 标题,在这种情况下,您可以附加到它。

于 2013-01-31T23:19:45.280 回答