16

Occasionally, I will run a command that has a lot of output. Occasionally, the last 30-40 lines of that output (a.k.a. the only part of the output I ever really see) is fine, but much further up, there was an error. I'd like to make it easier to notice that the command failed. To do so, I want the return code to be part of my prompt. So I took my $PS1:

[\D{%Y-%m-%d} \t] \[\e]0;\u@\h: \w\a\]\$

...and extended it to this:

[\D{%Y-%m-%d} \t] ${?/^0$/} \[\e]0;\u@\h: \w\a\]\$

This results in a prompt like this:

[2011-05-10 09:38:07] 0 soren@lenny:~$ 

However, I'd like to find a way to have it only include the exit code if it was non-0. How can I do that? Sure, I could use

$(echo \$? | sed -e blah)

but as lightweight as sed is, it's still quite a bit more heavy weight than bash's builtin stuff.

Soren
  • 753
  • 6
  • 11

9 Answers9

12

A little bit of printf abuse:

printf '%.*s' $? $?
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 2
    This both works and made me chuckle. I like it. I extended it slightly to this: `printf '%.*s%.*s' $? $? $? ' '` This make it add a space only if it's non-zero. – Soren May 10 '11 at 09:26
  • For the sake of copmleteness: using $(foo) in the primpt only evaluates foo once (not at each command). See http://stackoverflow.com/questions/16715103/bash-prompt-with-last-exit-code to see how to work around that. – sitaktif Sep 24 '13 at 06:28
  • 1
    Explanation: [What does “%.*s” mean in printf?](http://stackoverflow.com/q/7899119) – GingerPlusPlus Mar 07 '16 at 17:33
8

The following works for me:

PS1="[\D{%Y-%m-%d} \t] \u@\h:\w\a \${?##0} \$ "

example when $? is 0:

[2011-07-25 11:56:57] plars@plars-t500:~  $<br>
there is an extra space there ---------^^ not sure if that's a problem for you

example when $? is 130:

[2011-07-25 11:57:39] plars@plars-t500:~ 130 $
Community
  • 1
  • 1
pwlars
  • 516
  • 5
  • 4
6

You could use bash's built in pattern matching:

$ rc=0
$ echo ${rc##0}

$ rc=123
$ echo ${rc##0}
123
überjesus
  • 824
  • 7
  • 9
  • This was my first thought as well, but I rejected it thinking it would just strip off any 0's, but of course that's not the case. This seems to do the trick. The only downside is the superfluous space in case of a 0 exit code. – Soren May 10 '11 at 09:23
  • If you want to learn more about this, look under the Substring Removal section at http://www.tldp.org/LDP/abs/html/string-manipulation.html – wisbucky Mar 09 '17 at 19:07
4

Here's what I use in my .bashrc to get a red number with the exit code. Verbose, but it gets the job done and should be portable.

highlight()
{
    if [ -x /usr/bin/tput ]
    then
        tput bold
        tput setaf $1
    fi
    shift
    printf -- "$@"
    if [ -x /usr/bin/tput ]
    then
        tput sgr0
    fi
}

highlight_error()
{
    highlight 1 "$@"
}

highlight_exit_code()
{
    exit_code=$?
    if [ $exit_code -ne 0 ]
    then
        highlight_error "$exit_code "
    fi
}

PS1='$(highlight_exit_code)...'
l0b0
  • 55,365
  • 30
  • 138
  • 223
4

You can place an if-statement inside your PS1, which echoes out the exit status only if it is non-zero:

PS1='[\D{%Y-%m-%d} \t] $(es=$?; if [ $es -ne 0 ]; then echo $es; fi) \[\e]0;\u@\h: \w\a\]\$'
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 1
    This is the approach I use, as it allows an `else` clause. Note the importance of copying `$?` to a local variable, as `[` will over-write it before the `echo` is reached. – IMSoP Jun 20 '13 at 15:11
4

A common way to do this is to use trap ... ERR to execute arbitrary code when a command fails:

 $ trap 'echo $?' ERR
 $ true
 $ false
1
 $

A classic UNIX practical joke is trap 'echo You have new mail.' ERR

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
1

For zsh users: insert this into your PROMPT: %(?,, -%?-)

David Faure
  • 1,836
  • 15
  • 19
1

This is the prompt I use, insert into .bashrc and source it to use it. As you can see the PREV_RET_VAL is being appended to PS1 only in the case the value is not 0.

COLOR_RED_BOLD="\[\e[31;1m\]"
COLOR_GREEN_BOLD="\[\e[32;1m\]"
COLOR_NONE="\[\e[0m\]"

# prompt function
promptFunc()
{
PREV_RET_VAL=$?;

PS1=""

PS1="${PS1}\e[1;30m[\e[1;34m\u@\H\e[1;30m:\e[0;37m \e[0;32m\d \T\e[1;30m]\e[1;37m \w\e[0;37m\[\033]0; \w - \u@\H \007\]\n\[\] "

if test $PREV_RET_VAL -eq 0
then
    PS1="${PS1}${COLOR_GREEN_BOLD}\\$ ${COLOR_NONE}"
else
    PS1="${PS1}${COLOR_RED_BOLD}\\$ [${PREV_RET_VAL}] ${COLOR_NONE}"
fi
}

PROMPT_COMMAND=promptFunc
peret
  • 11
  • 1
1

Single quote example:

PS1='${?#0}> '

Double quote example (note the extra backslash to escape the $)

PS1="\${?#0}> "

Sample output:

> echo 'ok'
ok
> bogus
bogus: command not found
127> 

Explanation: ${var#pattern} is a bash parameter expansion that means remove the shortest matching pattern from the front of $var. So in this case, we are removing 0 from the front of $?, which would effectively truncate an exit code of 0.

If using double quotes, $? would be substituted when PS1 is set, instead of being evaluated each time. Do echo $PS1 to confirm you don't have a hardcoded value in PS1.

wisbucky
  • 33,218
  • 10
  • 150
  • 101