Python + bash prompt: how to print width calculation wrappers?

Question:

I wrote a Python script to replace "powerline" as a terminal prompt solution for myself here: https://github.com/diogobaeder/dotfiles/blob/master/.bash_prompt.py

Then all I do is to define the prompt from the output of that script:

# in my ~/.bashrc
export PS1="$(python ~/.bash_prompt.py)"

The script itself works fine, I get the command prompt I want; However, since there’s no wrapping for the styles I put there, the terminal (doesn’t matter which GUI terminal program I use) doesn’t calculate the prompt width correctly, and as I type characters after the prompt it ends up not wrapping them to a new line at first, overwriting the prompt completely.

Now, I know that when stylizing my bash prompt I need to escape style codes with [ and ] so that bash takes into consideration that they’re escape sequences and calculates the width correctly. However, if I put them as wrappers for my styles in my Python script (see esc_start and esc_end), I can’t get them to be properly evaluated by bash as "calculation escape sequences", instead I get literal square brackets printed. If I then escape in Python the backslashes too (\[ and \]), then I get unescaped literals outputted ([ and ]). Bash seems to completely ignore them as escape sequences for calculating the prompt width.

If, however, I remove the backslash in my PS1 command ($(python ~/.bash_prompt.py) instead of $(python ~/.bash_prompt.py)), then putting [ and ] as escape sequences in my Python script (as esc_start and esc_end), then bash considers them as proper escapes and ends up wrapping lines as expected (i.e., when I go past the right border it wraps the line as expected). The problem with this, however, is that removing this backslash from my PS1 definition in .bashrc makes the script run only once per terminal session, and not for each prompt line – so, for example, if I’m in a Git working tree and I change from one branch to another, it doesn’t show the new branch as soon as the command finishes, instead it shows the old branch.

Let me give some examples of what I mean, that you can try in your own .bashrc without needing my Python script:

PS1="[33[31m]u@h[33[0m]$ " # This wraps lines correctly
PS1="33[31mu@h33[0m$ " # This makes the line overwrite the prompt

So, any ideas of how to cope with bash and make it understand the [ and ] sequences correctly when printed by the Python script, while still keeping the script running for each command prompt? Or, if this is a limitation in bash, is there another way to force it to wrap the line when it reaches the right border of the terminal window?

Thanks!
Diogo

Asked By: diogobaeder

||

Answers:

Bash first interprets the escape sequences in $PS1 and only afterwards handles command substitution, etc.

Bash allows these prompt strings to be customized by inserting a number of backslash-escaped special characters that are decoded as follows […]
After the string is decoded, it is expanded via parameter expansion, command substitution, arithmetic expansion, and quote removal […]

Bash Reference Manual: Controlling the Prompt

This means that any special sequences printed by your command will not be interpreted as colors, etc. The solution is to use $PROMPT_COMMAND to change the value of $PS1, like:

PROMPT_COMMAND='PS1=$(python ~/.bash_prompt.py)'
Answered By: Grisha Levit
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.