Is there an efficient way to colorize the text in a bash prompt without two calls?

Efficient way to colorize text in a bash prompt without two calls?

There are many resources around setting up your PS1. The critical points are here:

  • You can call a custom function to generate text, resulting in custom text
  • It is possible that such a function produces custom color codes.
  • Non-printable text (such as color codes) must be marked to do word wrapping work.
  • Cannot perform this marking with a custom function.

Perhaps I am mistaken at the last point, and if there is a way, this will perfectly solve this.

Here's a simplified and somewhat far-fetched example that is topologically similar, if that makes sense, to the one I'm messing with. I have an external command (call it generate_text ) that gives stdout either "OK" or some kind of one-word message. It may also not emit anything at all. These three states should appear in the tooltip: if it does not emit anything, leave the tooltip exactly as it is ( user@hostname :path$ ); if it emits OK, put a green “OK” before the invitation; if something else, put this text in red.

My current solution is to create a custom function that calls generate_text and either emits its text or emits a color code on it and then calls this function twice:

 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 

This means that I have to call generate_text twice and assume that they will return the same string (which will usually be the case, but it is theoretically possible that the state can change between two calls). It seems wasteful to me. Is there a convenient way to emit both a non-printable color code and part of the text from the same function?

+1
source share
2 answers

You can use PROMPT_COMMAND to calculate all the necessary values, and then use them at your prompt:

 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 

Note that PROMPT_COMMAND is sometimes already configured to set the xterm header, in which case you can add it.

+3
source

This is undocumented, so this is probably not a good solution, but seems to work. It uses the bash hint's internal knowledge, which indicates that the text between the characters \ 001 and \ 002 is not taken into account in relation to the number of visible characters in the prompt line.

 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 

The bash source, which extends the prompt line, contains the following comment, which was used to formulate the above solution:

 /* 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'. */ 
+3
source

Source: https://habr.com/ru/post/972495/


All Articles