10

I've added the return value of the last command to PS1 (aka "the prompt") in my .bashrc.

Now I'd like to have it shown only if the value is nonzero.

Android's shell has it:

${| local e=$? (( e )) && REPLY+="$e|" return $e }

Question: how to convert it to bash?

0

4 Answers 4

15
PS1='${?#0}$ '

It uses a special form of parameter expansion, ${?#0}, which means: "Remove the character zero if it is the first character of ${?}, the exit code of the previous command."

You can also change the color of the prompt if the last exit code were not zero:

PS1='\[\e[0;$(($?==0?0:91))m\]$ \[\e[0m\]'

Prompt

That uses a if-else ternary expression $(($?==0?0:91)) that makes the color code 0;91m (red, see color codes) if the last command exits with non-zero, or 0;0m (your default color) otherwise.

6
  • 11
    My favourite thing about bash is how readable and obvious it is... Jun 9, 2021 at 10:40
  • 1
    Unfortunately this doesn’t seem to work if the PS1 also contains subshell invocations (simplified example: PS1='$(echo -n)\[\e[0;$(($??0:91))m\]\$ \[\e[0m\]') — in this case, the return code is always 0 inside the PS1, even though I can query $? on the command line and it has the correct value. Jun 9, 2021 at 11:12
  • 1
    @KonradRudolph In that case, just save $? in a variable: PS1='_ps1err=$?;...;$((_ps1err?0:91))....
    – Kusalananda
    Jun 9, 2021 at 11:45
  • @Kusalananda Yes, that’s what I ended up doing (inside $((…)), what you’ve posted won’t work directly). But then I’m getting the status echoed in my shell prompt. Jun 9, 2021 at 12:07
  • 2
    @AdamBarnes The ugly parts here are all related to (virtual) terminals and how to get them to do other things than to display plain foreground-coloured text on background-coloured background. All programming languages face the same issue – unless they abstract that part away behind some library API like termcap or curses. You can certainly find or write one in Bash if you want to. Jun 9, 2021 at 18:39
8

This is what I use in my .bashrc:

PS1_PROMPT() {
  local e=$?
  (( e )) && printf "$e|"
  return $e
}
PS1='$(PS1_PROMPT)'"$PS1"
ibug@example:~ $ false
1|ibug@example:~ $ ^C
130|ibug@exame:~ $ true
ibug@example:~ $

This has the advantage over JoL's answer that the value of $? is preserved after displaying (via return $e). It's also basically a rework of Android's default /system/etc/mkshrc that does the same job.

Note that $(PS1_PROMPT) is enclosed in single quotes because $PS1 is evaluated every time it's printed, in addition to when set in a variable assignment expression, so single quotes prevents it from being evaluted right now and instead defers that to when printing.

Alternatively, as suggested by ilkkachu, you can make use of Bash PROMPT_COMMAND special variable:

PS1_PROMPT() {
  local e=$?
  PROMPT_ECODE=
  (( e )) && PROMPT_ECODE="$e|"
  return $e
}
PROMPT_COMMAND=PS1_PROMPT
PS1='$PROMPT_ECODE'"$PS1"

This has one advantage that no subshell is spawned every time PS1 is printed.

0
4

The entire Android PS1 is:

${|
    local e=$?

    (( e )) && REPLY+="$e|"

    return $e
}$HOSTNAME:${PWD:-?} $ 

I don't recognize ${|, but this bash is equivalent:

PS1='$(e=$?; (( e )) && echo "$e|")$HOSTNAME:${PWD:-?} $ '

local is not needed because $() is a subshell.

1
  • 2
    FYI: ${| cmds } is MirBSD Korn Shell (mksh)-specific (ref).
    – iBug
    Jun 9, 2021 at 9:39
1

Thank you, everyone, that's what I settled for:

PS1_PROMPT() {
  local e=$?
  #(( e )) && printf "$e|" # BW
  (( e )) && printf "\033[01;31m$e\033[00m|"  # color
  return $e
}
PS1='$(PS1_PROMPT)'"$PS1"

This displays errno, a.k.a. $?, in red if it's different than 0.

Note that the condition found in .bashrc, "$color_prompt" = yes could not be used above, because the function PS1_PROMPT() is not evaluated in .bashrc, but each time in the prompt. I did not want to export $color_prompt systemwide.

5
  • You don't have to export color_prompt, just keep it around for the duration of the shell. Though you could use it in bashrc to determine which version of the function to define, or just the prompt string. Also note that you'd need to wrap the color setting within \[ and \] for Bash to calculate the prompt width correctly.
    – ilkkachu
    Jun 10, 2021 at 10:52
  • @ilkachu, this won't work. This is the reason why: PS1='$(PS1_PROMPT)' Jun 11, 2021 at 12:32
  • I'm not sure what you're trying to say doesn't work, but using e.g. (( e )) && [ "$color_prompt" ] && printf "\033[01;31m$e\033[00m|" inside the function works fine as long color_prompt is set in the shell, it doesn't need to be exported. And by "works fine", I mean works to set that string to the prompt. It still gets the prompt length wrong, because the \[ .. \] guards aren't set properly. (which you'll see if you try to enter a command line long enough to wrap)
    – ilkkachu
    Jun 11, 2021 at 12:55
  • The function PS1_PROMPT is in single quotation marks, meaning the contents of the function is not evaluated during the running of .bashrc, but each time the prompt is displayed. HTH. Jun 11, 2021 at 19:10
  • 1
    yes. You don't need to export it (which would make it end up in the environment of all programs the shell launches), just keep it set for the duration of the shell (in which case it doesn't get passed to any other programs).
    – ilkkachu
    Jun 11, 2021 at 23:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .