How do I include return code/status in BASH prompt entry?

I’m unable to use my default BASH prompt (inside configuration.nix) as-is, and had to modify it, including removing the part about monitoring the return status of the previous command. I can’t get it to use variables properly, which would be evaluated live on each new prompt line, not once upon logging-in.

Any ideas? I’d include my normal default prompt, but it’s pretty darn long (blast you BASH! ZSH is like 3 lines to do the same thing. LOL).

Thanks!

Edit: I just went ahead and created a simple .bashrc (and .profile pointing to .bashrc) with my normal prompt. It overrides the defined one, as it should. But I’d still like to know how to do it in configuration.nix, as it’s my understanding that’s how we are supposed to be doing things?

Hi,

It would be way easier to help you if you shared some
configuration snippets.

From what you describe, it looks like an escaping problem with
$ signs.

1 Like

Darn, I thought you were onto something, there. It sort of made sense, since nix might be looking for variables of it’s own, and would have to escape variables meant for the prompt. Alas, I still get “undefined variable” on all of them.

Okay, here is config (snip) as I made it, which won’t build. (The original, without escaping the $)

  programs = {
    bash = {
      promptInit = ''
        # Set Window Title
        TITLE='\001\033]0;${SHLVL} - [\s \V] \u: ${PWD}\007\002'
        PS0='\001\033]0;$(builtin history 1)\007\002'

        # Set Colors
        BRBLUE='\001\033[01;34m\002'
        BRYELLOW='\001\033[01;33m\002'
        BRRED='\001\033[01;31m\002'
        BRGREEN='\001\033[01;32m\002'
        CRESET='\001\033[00m\002'

        # Trim Directories
        PROMPT_DIRTRIM=2

        # Use PROMPT_COMMAND to get Return Status
        function prompt_command {
          RET=$?
          if [[ ${RET} -ne 0 ]]; then
            ERR_CLR=$BRRED
          else
            ERR_CLR=$BRGREEN
          fi
        }
        PROMPT_COMMAND=prompt_command

        # Set User Colors
        if [[ ${EUID} -eq 0 ]] ; then
          CWD_USER=$BRYELLOW
          USR_USER=$BRRED
        else
          CWD_USER=$BRBLUE
          USR_USER=$BRGREEN
        fi

        # Combine Window Title and Prompt
        PS1=${TITLE}'$(echo -ne "$CWD_USER\w$BRYELLOW >$USR_USER>$ERR_CLR>$CRESET ")'
      '';
      shellAliases = commonAliases;
    };
  };

Here’s the awful mess that I got to build without errors, but it makes a complete mess of /etc/bashrc, and throws a ton of errors when logging in (due to all the extra escapes):

  programs = {
    bash = {
      promptInit = ''
        # Set Window Title
        TITLE=\'\\001\\033]0;\$\{SHLVL\} - [\\s \\V] \\u: \$\{PWD\}\\007\\002\'
        PS0=\'\\001\\033]0;\$\(builtin history 1\)\\007\\002\'

        # Set Prompt
        # Set Colors
        BRBLUE=\'\\001\\033[01;34m\\002\'
        BRYELLOW=\'\\001\\033[01;33m\\002\'
        BRRED=\'\\001\\033[01;31m\\002\'
        BRGREEN=\'\\001\\033[01;32m\\002\'
        CRESET=\'\\001\\033[00m\\002\'

        # Trim Directories
        PROMPT_DIRTRIM=2

        # Use PROMPT_COMMAND to get Return Status
        function prompt_command {
          RET=\$?
          if [[ \$\{RET\} -ne 0 ]]; then
            ERR_CLR=\$BRRED
          else
            ERR_CLR=\$BRGREEN
          fi
        }
        PROMPT_COMMAND=prompt_command

        # Set User Colors
        if [[ \$\{EUID\} -eq 0 ]] ; then
          CWD_USER=\$BRYELLOW
          USR_USER=\$BRRED
        else
          CWD_USER=\$BRBLUE
          USR_USER=\$BRGREEN
        fi

        # Combine Window Title and Prompt
        PS1=\$\{TITLE\}\'\$\(echo -ne \"\$CWD_USER\\w\$BRYELLOW >\$USR_USER>\$ERR_CLR>\$CRESET \"\)\'
      '';
      shellAliases = commonAliases;
    };
  };

The escapes are wrong. The only sequences that have special meaning in '' strings are ${ (antiquotation) and '' itself. Backslashes aren’t special. You don’t appear to have any embedded '' sequences so all you have to care about are ${foo}, and these get escaped as ''${foo}. Alternatively you could remove the {} characters whenever bash doesn’t actually require them (which I’m pretty sure is everywhere you use them).

Escaping the sequences looks something like the following. Also note how $(builtin history 1) doesn’t need escaping because it’s ${ that’s special, not just $.

  programs = {
    bash = {
      promptInit = ''
        # Set Window Title
        TITLE='\001\033]0;''${SHLVL} - [\s \V] \u: ''${PWD}\007\002'
        PS0='\001\033]0;$(builtin history 1)\007\002'

        # Set Colors
        BRBLUE='\001\033[01;34m\002'
        BRYELLOW='\001\033[01;33m\002'
        BRRED='\001\033[01;31m\002'
        BRGREEN='\001\033[01;32m\002'
        CRESET='\001\033[00m\002'

        # Trim Directories
        PROMPT_DIRTRIM=2

        # Use PROMPT_COMMAND to get Return Status
        function prompt_command {
          RET=$?
          if [[ ''${RET} -ne 0 ]]; then
            ERR_CLR=$BRRED
          else
            ERR_CLR=$BRGREEN
          fi
        }
        PROMPT_COMMAND=prompt_command

        # Set User Colors
        if [[ ''${EUID} -eq 0 ]] ; then
          CWD_USER=$BRYELLOW
          USR_USER=$BRRED
        else
          CWD_USER=$BRBLUE
          USR_USER=$BRGREEN
        fi

        # Combine Window Title and Prompt
        PS1=''${TITLE}'$(echo -ne "$CWD_USER\w$BRYELLOW >$USR_USER>$ERR_CLR>$CRESET ")'
      '';
      shellAliases = commonAliases;
    };
  };

But in this case it’s probably better to just turn all ${foo} sequences into $foo, as in

  programs = {
    bash = {
      promptInit = ''
        # Set Window Title
        TITLE='\001\033]0;$SHLVL - [\s \V] \u: $PWD\007\002'
        PS0='\001\033]0;$(builtin history 1)\007\002'

        # Set Colors
        BRBLUE='\001\033[01;34m\002'
        BRYELLOW='\001\033[01;33m\002'
        BRRED='\001\033[01;31m\002'
        BRGREEN='\001\033[01;32m\002'
        CRESET='\001\033[00m\002'

        # Trim Directories
        PROMPT_DIRTRIM=2

        # Use PROMPT_COMMAND to get Return Status
        function prompt_command {
          RET=$?
          if [[ $RET -ne 0 ]]; then
            ERR_CLR=$BRRED
          else
            ERR_CLR=$BRGREEN
          fi
        }
        PROMPT_COMMAND=prompt_command

        # Set User Colors
        if [[ $EUID -eq 0 ]] ; then
          CWD_USER=$BRYELLOW
          USR_USER=$BRRED
        else
          CWD_USER=$BRBLUE
          USR_USER=$BRGREEN
        fi

        # Combine Window Title and Prompt
        PS1=$TITLE'$(echo -ne "$CWD_USER\w$BRYELLOW >$USR_USER>$ERR_CLR>$CRESET ")'
      '';
      shellAliases = commonAliases;
    };
  };
1 Like

I’ve looked high and low over the years for concrete information on exactly what needs escaping where, and it apparently just doesn’t exist. Even SuperUser, StackOverflow, etc. all say that there is no definitive list. Everyone just has to wing it.

Is this escape behavior mentioned in any manual somewhere? Anyway, I do appreciate your help, and will give this a try! BRB…

Thank you @lilyball ! Removing the braces around variables worked like a charm! So, inside double single-quotes, escape ${. At least as far as nix is concerned. Got it. Thanks! :+1:

It’s documented in the manual, though admittedly it’s not the most clear documentation.

1 Like

Indeed! Thanks!

Since ${ and '' have special meaning in indented strings, you need a way to quote them. $ can be escaped by prefixing it with '' (that is, two single quotes), i.e., ''$ . '' can be escaped by prefixing it with ' , i.e., ''' . $ removes any special meaning from the following $ . Linefeed, carriage-return and tab characters can be written as ''\n , ''\r , ''\t , and ''\ escapes any other character.