4

I'm trying to customize my bash prompt and I'm having trouble with a few conditionals.

My current PS1 looks like this.

export PS1="\
$PS1USERCOLOR\u\
$COLOR_WHITE@\
$COLOR_GREEN\h\
$COLOR_WHITE:\
$COLOR_YELLOW\W\
\`if type parse_git_branch > /dev/null 2>&1; then parse_git_branch; fi\`\
\`if [ \$? = 0 ]; then echo -e '$COLOR_WHITE'; else echo -e '$COLOR_RED'; fi\`\$\     
$COLOR_WHITE"
  • The first 6 lines just set regular PS1 stuff.

  • Line 7 then calls a function to display the current git branch and status if applicable.

  • Line 8 then tests the return code of the previous command and changes the colour of the $ on the end.

  • Line 9 sets the prompt back to white ready for the user's command.

However line 8 is responding to the return code from line 7's function and not the previous command as I first expected.

I've tried moving line 8 before line 7 and eveything works as it should. But I don't want line 8 before line 7, the $ must be on the end.

I've tried setting a variable earlier on to be the value of $? and then testing that variable like so

export PS1="\
\`RETURN=\$?\`\
$PS1USERCOLOR\u\
$COLOR_WHITE@\
$COLOR_GREEN\h\
$COLOR_WHITE:\
$COLOR_YELLOW\W\
\`if type parse_git_branch > /dev/null 2>&1; then parse_git_branch; fi\`\
\`if [ \$RETURN = 0 ]; then echo -e '$COLOR_WHITE'; else echo -e '$COLOR_RED'; fi\`\$\     
$COLOR_WHITE"

But this doesn't work.

Does anybody have any idea how to solve my problem?

Jacob Tomlinson
  • 3,341
  • 2
  • 31
  • 62

3 Answers3

4

The proper way is to use PROMPT_COMMAND like so:

prompt_cmd () {
    LAST_STATUS=$?
    PS1="$PS1USERCOLOR\u"
    PS1+="$COLOR_WHITE@"
    PS1+="$COLOR_GREEN\h"
    PS1+="$COLOR_WHITE:"
    PS1+="$COLOR_YELLOW\W"
    if type parse_git_branch > /dev/null 2>&1; then
        PS1+=$(parse_git_branch)
    fi
    if [[ $LAST_STATUS = 0 ]]; then
        PS1+="$COLOR_WHITE"
    else
        PS1+="$COLOR_RED"
    fi
    PS1+='\$'
    PS1+="$COLOR_WHITE"
}

Since PROMPT_COMMAND is evaluated prior to each prompt, you simply execute code that sets PS1 they way you like for each prompt instance, rather than trying to embed deferred logic in the string itself.

A couple of notes:

  1. You must save $? in the first line of the code, before the value you want is overwritten.
  2. I use double quotes for most of the steps, except for \$; you could use PS1+="\\\$" if you like.
chepner
  • 497,756
  • 71
  • 530
  • 681
2

The standard solution to this problem is to make use of the bash environment variable PROMPT_COMMAND. If you set this variable to the name of a shell function, said function will be executed before each bash prompt is shown. Then, inside said function, you can set up whatever variables you want. Here's how I do almost exactly what you're looking for in my .bashrc:

titlebar_str='\[\e]0;\u@\h: \w\a\]'
time_str='\[\e[0;36m\]\t'
host_str='\[\e[1;32m\]\h'
cwd_str='\[\e[0;33m\]$MYDIR'
git_str='\[\e[1;37m\]`/usr/bin/git branch --no-color 2> /dev/null | /bin/grep -m 1 ^\* | /bin/sed -e "s/\* \(.*\)/ [\1]/"`\[\e[0m\]'
dolr_str='\[\e[0;`[ $lastStatus -eq 0 ] && echo 32 || echo 31`m\]\$ \[\e[0m\]'
export PS1="$titlebar_str$time_str $host_str $cwd_str$git_str$dolr_str"

function prompt_func {
    # Capture the exit status currently in existence so we don't overwrite it with
    # any operations performed here.
    lastStatus=$?

    # ... run some other commands (which will have their own return codes) to set MYDIR
}

export PROMPT_COMMAND=prompt_func

Now bash will run prompt_func before displaying each new prompt. The exit status of the preceding command is captured in lastStatus. Because git_str, dolr_str, etc. are defined with single quotes, the variables (including lastStatus) and commands inside them are then re-evaluated when bash dereferences PS1.

dg99
  • 5,456
  • 3
  • 37
  • 49
  • Using `PROMPT_COMMAND`, you would typically just conditionally set the value of, e.g, `dolr_str` inside the function, rather than try to embed a command in the value of `dolr_str`. – chepner Jul 03 '14 at 20:46
  • True. The above is the result of many edits done in many differing states of mind. :) – dg99 Jul 03 '14 at 20:53
0

Solved it!

I need to use the PROMPT_COMMAND variable to set the RETURN variable. PROMPT_COMMAND is a command which is called before PS1 is loaded.

My script now looks like

PROMPT_COMMAND='RETURN=$?'

export PS1="\
$PS1USERCOLOR\u\
$COLOR_WHITE@\
$COLOR_GREEN\h\
$COLOR_WHITE:\
$COLOR_YELLOW\W\
\`if type parse_git_branch > /dev/null 2>&1; then parse_git_branch; fi\`\
\`if [[ \$RETURN = 0 ]]; then echo -e '$COLOR_WHITE'; else echo -e '$COLOR_RED'; fi\`\$\     
$COLOR_WHITE"
Jacob Tomlinson
  • 3,341
  • 2
  • 31
  • 62