7

I'm using a function to dynamically generate output for PS1. There are a couple statements that check if node and package.json exist, and if git and .git folder exist to display node version or the git branch. If none exist it just outputs the User$.

The problem is when user goes into another folder where none of the conditions are met the prompt is not updating. It's like conditions are cached or smth

function displayPS1() {
  MESSAGE="";
  GRAY_BACK="\[\e[100;97m\]";
  GREEN_BACK="\[\e[100;42m\]";
  GREEN_FORE="\[\e[32;1m\]";
  CYAN_BACK="\[\e[100;46m\]";
  CYAN_FORE="\[\e[36;1m\]";
  RESET="\[\e[0m\]";

  if hash node 2>/dev/null && [ -e package.json ]; then
    NODE='$(node -v | sed "s/\(v[0-9]*\)\(\.[0-9]*\.[0-9]*\)/\1/g")';
    MESSAGE="${GRAY_BACK} node ${GREEN_BACK} $NODE ${RESET} User${GREEN_FORE}$ ${RESET}";
  elif hash git 2>/dev/null && [ -d .git ]; then
    BRANCH='$(cat .git/HEAD | sed "s/ref:[[:space:]]refs\/heads\///")';
    MESSAGE="${GRAY_BACK} git ${CYAN_BACK} $BRANCH ${RESET} User${CYAN_FORE}$ ${RESET}";
  else
    MESSAGE="User${CYAN_FORE}$ ${RESET}";
  fi

  echo "$MESSAGE";
}
export PS1=$(displayPS1);
  • You need to include the conditions in the PS1 (e.g. using $(...)). – choroba Jul 21 '19 at 13:07
  • 1
    Apart from anything else, PS1 really does not need to be exported. It's just the shell using it. Also, running node at least once, possibly twice for each prompt? Would it not be better to overload the cd command? The prompt would not need to be re-evaluated unless you change directory (most of the time at least). – Kusalananda Jul 21 '19 at 14:14
  • I like the idea with the cd. But what if someone removes package.json or node or git, we would need to overload those commands also @Kusalananda – Mr. Madamin Jul 21 '19 at 14:49
  • @Mr.Madamin Um? I'm not sure I follow how that's special if overloading cd? I was thinking something simple, like cd () { builtin cd "$@"; PS1=$(displayPS1); }. – Kusalananda Jul 21 '19 at 14:51

2 Answers2

14
export PS1=$(displayPS1);

This will run displayPS1, and the if statements within once, assigning the result to the prompt. The conditions won't be processed again after that.

Instead, put the function call in PROMPT_COMMAND, so it gets called every time the prompt is going to be printed. So either

PROMPT_COMMAND='PS1=$(displayPS1)'

or perhaps rather

PROMPT_COMMAND=setPS1

and make setPS1 a function that sets PS1 itself. (Getting rid of the command substitution saves a fork from the subshell invocation every time the prompt is changed.)

ilkkachu
  • 138,973
5

Use the quotes.

PS1='$(displayPS1)'

If you don't then the function is evaluated at assignment time.

Kusalananda
  • 333,661
  • It won't work, since it will escape the colors – Mr. Madamin Jul 21 '19 at 14:46
  • @Mr.Madamin Is that not an issue with your echo at the end of the function (something that would be an issue regardless of when the function is evaluated)? Use echo -e instead of plain echo. You also seem to use single-quoted command substitutions in your code. Why? – Kusalananda Jul 21 '19 at 15:02
  • @Kusalananda, no, because Bash handles backslash-escapes in the prompt. But it does so before processing variable and command substitutions, so putting the function inside one breaks them. In particular, it breaks \[ .. \], since you can't produce them with echo -e or such, they need to be in PS1. – ilkkachu Jul 21 '19 at 15:15
  • @ilkkachu Fair enough. You're talking the the guy who uses $ for prompt :-) – Kusalananda Jul 21 '19 at 15:19
  • @Kusalananda, colors are optional, but I couldn't live without the working directory being included in the prompt... – ilkkachu Jul 21 '19 at 15:20
  • What's wrong with $ for prompt ? :) @ilkkachu – Mr. Madamin Jul 21 '19 at 15:39
  • I used single quote inside in order to re-evaluate commands like node and git, and left colors separately. I found this solution on the web, as you can see, I'm a beginner in shell @Kusalananda – Mr. Madamin Jul 21 '19 at 15:41