5

I wanted to modify my PS1 to run some commands every time. Let's say I want it so that if the last executed command was successful, it would add a green smile at the end of PS1, otherwise the smile should be red.
I extracted it to a function:

function exit_smile {
EXITSTATUS="$?"
RED="\[\e[1;31m\]"
GREEN="\[\e[32;1m\]"

if [ "${EXITSTATUS}" -eq 0 ]
then
   SMILE="${GREEN}:)"
else
   SMILE="${RED}:("
fi

echo -n "$SMILE"

}

and then tried both using `exit_smile` and \$(exit_smile) when modifying the PS1 variable, but it executes it once when modifying PS1 or prints literal \[\e...\] instead of a color.
For example

PROMPT="\u@\h \W"
PS1="${PROMPT} \$ \$(exit_smile) ${OFF}\n"

Gives username@hostname ~ $ \[\e[32;1m\]:)
What am I missing?

terdon
  • 242,166
Szymon
  • 163

1 Answers1

4

I'm not sure if this has changed between versions(*), but my man page for Bash says that

Bash allows these prompt strings to be customized by inserting a number of backslash-escaped special characters that are decoded as follows:

(list contains \e, \[, \] etc.)

After the string is decoded, it is expanded via parameter expansion, command substitution, ...

Which means that the \[..\] can't come from the command substitution, but must be there before that.

(It also means you could use \u or \w as arguments to a command substitution, and they'd get replaced before the command runs. And I have no idea what putting \[..\] inside a command substitution would do... This would make more sense the other way around.)

So, we'll have to put the color codes in separate expansions and protect them with \[..\] by hand. I'll use variables instead of command substitution, and also the $'...' expansion to get the ESC character:

prompt_smile() {
        if [ "$?" = 0 ] ; then
                smile=' :) '
                smilecolor=$'\e[1;32m'
        else
                smile=' :( '
                smilecolor=$'\e[1;31m'
        fi
        normalcolor=$'\e[0m'
}

PROMPT_COMMAND=prompt_smile
PS1='\u@\h \W \$ \[$smilecolor\]$smile\[$normalcolor\]\n'

(* the reason I wonder about that, is that the answers to the older and similar but no so duplicate question seem to output the \[..\] from within an expansion)

ilkkachu
  • 138,973
  • Thank you. It's not an ideal solution, as it leaves variables smile and smilecolor public, but at least it works. – Szymon Dec 04 '17 at 07:59
  • @Szymon, they're only visible within your shell (unless you export them). Rename them something like __p_smile etc, and it'll be quite hard to hit them accidentally. – ilkkachu Dec 04 '17 at 08:37