I have a fancy prompt with colors, and now bash doesn't seem to know how wide my terminal is. Lines wrap around incorrectly.
This is usually caused by incorrect use of \[ and \]
For all the following examples, assume these variables are set:
Escape the colors with \[ \]
You must put \[ and \] around any non-printing escape sequences in your prompt. Thus:
1 PS1='\[$host_color\]\h\[$reset\]:\[$dir_color\]\w\[$reset\]\$ '
The \[ \] tells bash that the bytes within will not increase the size of the prompt. 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.
Functions that output colored text
The \[ and \] are parsed before any of the expansions take place, so the following will not work:
This will result in the prompt looking like
myhost:~/repo (\[\]main\[\])$
There are several ways to work around this problem:
Using ${parameter@P}
Since bash 4.4, ${parameter@P} will expand a variable the same way PS1 is expanded:
This can also be used to provide a colored prompt for the read command when using its readline mode (-e):
Hardcoding \001 and \002
The \[ and \] actually just gets replaced with byte values 1 and 2, respectively, which are the markers the readline library uses to know which parts should be ignored when calculating the prompt length.
This can also be used to provide a colored prompt for the read command when using its readline mode (-e) in bash versions older than 4.4:
Using PROMPT_COMMAND
The value of the special PROMPT_COMMAND variable will be evaluated just before the PS1 prompt is displayed. This can be used to assign variables that the prompt then can use conditionally:
Checking if your PS1 variable is correctly set
The following code can tell you if PS1 is correctly set:
1 tput clear; sed $'s/\1[^\2]*\2//g; h; s/./=/g; H; x' <<< "${PS1@P}"
It first clears the terminal, then sed prints the prompt with all the colors removed, along with a bar showing the width.
Example showing a correct prompt
# Correct
PS1='\[$host_color\]\h\[$reset\]:\[$dir_color\]\w\[$reset\]\$'
tput clear; sed $'s/\1[^\2]*\2//g; h; s/./=/g; H; x' <<< "${PS1@P}"the result will be
myhost:~$ <- uncolored version of the prompt ========= <- shows the calculated length myhost:~$ <- the actual prompt
When the first line is an uncolored, but otherwise identical, version of the actual prompt, and the bar lines up exactly with both, you have a correctly set PS1 variable.
Example showing a prompt that is assumed to be longer than it actually is
# Wrong
PS1='$host_color\]\h\[$reset\]:\[$dir_color\]\w\[$reset\]\$'
tput clear; sed $'s/\1[^\2]*\2//g; h; s/./=/g; H; x' <<< "${PS1@P}"In this example, the leading \[ is missing, so $host_color is not properly enclosed
myhost:~$ <- green instead of uncolored =============== <- longer than the visible prompt myhost:~$
where the first line is green instead of uncolored, and the bar is longer than the actual prompt, because the escape sequence for green (ESC, [, 3, 2, and m) gets included in the calculation of the prompt's width.
Example showing a prompt that is assumed to be shorter than it actually is
# Wrong
PS1='\[$host_color\h\]\[$reset\]:\[$dir_color\]\w\[$reset\]\$'
tput clear; sed $'s/\1[^\2]*\2//g; h; s/./=/g; H; x' <<< "${PS1@P}"In this example, the \h is inside a pair of \[ \]. The result becomes
:~$ <- uncolored, but lacks the hostname part === <- shorter than the visible prompt myhost:~$
the first line is uncolored, but missing the hostname part. The prompt is now assumed to be just three columns wide.
Other causes
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
