I have a fancy prompt with colors, and now bash doesn't seem to know how wide my terminal is. Lines wrap around incorrectly.

Escape the colors with \[ \]

You must put \[ and \] around any non-printing escape sequences in your prompt. Thus:

   1 fancy_prompt() {
   2   local blue=$(tput setaf 4)
   3   local purple=$(tput setaf 5)
   4   local reset=$(tput sgr0)
   5   PS1="\\[$blue\\]\\h:\\[$purple\\]\\w\\[$reset\\]\\\$ "
   6 }

Without the \[ \], bash will think the bytes which constitute the escape sequences for the color codes will actually take up space on the screen, so bash won't be able to know where the cursor actually is.

Escape the colors with \001 \002 (dynamic prompt or read -p)

The \[ \] are only special when you assign PS1, if you print them inside a function that runs when the prompt is displayed it doesn't work. In this case you need to use the bytes \001 and \002:

   1 # this function runs when the prompt is displayed
   2 active_prompt () {
   3   local blue=$(tput setaf 4)
   4   local reset=$(tput sgr0)
   5   printf '\001%s\002%s\001%s\002' "$blue" "$PWD" "$reset"
   6 }
   8 PS1='$(active_prompt)\$ '

If you want to use colors in the "read -p" prompt, the wrapping problem also occurs and you cannot use \[ \], you must also use \001 \002 instead:

   1 blue=$(tput setaf 4)
   2 reset=$(tput sgr0)
   3 IFS= read -rp $'\001'"$blue"$'\002''what is your favorite color?'$'\001'"$reset"$'\002' answer

If you still have problems, e.g. when going through your command history with the Up/Down arrows, make sure you have the checkwinsize option set:

   1 shopt -s checkwinsize

Refer to the Wikipedia article for ANSI escape codes.

More generally, you should avoid writing terminal escape sequences directly in your prompt, because they are not necessarily portable across all the terminals you will use, now or in the future. Use tput to generate the correct sequences for your terminal (it will look things up in your terminfo or termcap database).

Since tput is an external command, you want to run it as few times as possible, which is why we suggest storing its results in variables, and using those to construct your prompt (rather than putting $(tput ...) in PS1 directly, which would execute tput every time the prompt is displayed). The code that constructs a prompt this way is much easier to read than the prompt itself, and it should work across a wide variety of terminals. (Some terminals may not have the features you are trying to use, such as colors, so the results will never be 100% portable in the complex cases. But you can get close.)

BashFAQ/053 (last edited 2022-09-14 02:03:48 by emanuele6)