meustrus
meustrus

Reputation: 7315

Set PS1 with subcommand that prints colors

When putting ANSI color codes in PS1, they need to be surrounded with \[\] or else the prompt can get confused about where the editable part of the line starts. However, when a subcommand ($()) prints colors, the \[\] escapes are always being written literally to the prompt...and with long enough commands in my history, the prompt gets confused.

Here's an example:

ps1test() {
    ps1sub() {
        printf '\[\033[32m\]Hello!\[\033[0m\]'
    }
    PS1='$(ps1sub) \$ '
}

Expected:

$ ps1test
Hello! $

Actual (bash installed by Git for Windows):

$ ps1test
\[\]Hello!\[\] $

How can I get my shell to interpret the \[\] escapes from a subcommand?

Upvotes: 1

Views: 485

Answers (3)

dimo414
dimo414

Reputation: 48794

If you're trying to create a dynamic prompt, you're likely to have an easier time setting the PS1 value via a function invoked as PROMPT_COMMAND, e.g.:

ps1test() {
  ps1sub() {
        printf '\[\033[32m\]Hello!\[\033[0m\]'
    }
    PS1="$(ps1sub)"' \$ ' # notice the double-quote
}
PROMPT_COMMAND=ps1test

This renders correctly as Hello! $ for me.

I use prompt.gem to render my prompt, you can take a look at how it configures PROMPT_COMMAND for some inspiration.

Upvotes: 2

William Pursell
William Pursell

Reputation: 212178

This is exactly the right use case for eval:

ps1test() {  
    ps1sub() {  
        printf '\[\033[31m\]Hello!\[\033[0m\]';     
    };  
    eval PS1="'$(ps1sub) \$ '"; 
}

Upvotes: 0

that other guy
that other guy

Reputation: 123400

Only \[s in the literal string are interpreted. \[s resulting from embedded expansions are not.

The easiest way to get around it is to have PROMPT_COMMAND set a PS1 to a new literal value each time:

updateps1() {
    ps1sub() {
        printf '\[\033[32m\]Hello $RANDOM!\[\033[0m\]'
    }
    PS1="$(ps1sub) \\\$ "
}

PROMPT_COMMAND='updateps1'

Upvotes: 3

Related Questions