How to set a conditional newline in PS1?

2019-03-24 16:15发布

问题:

I am trying to set PS1 so that it prints out something just right after login, but preceded with a newline later.

Suppose export PS1="\h:\W \u\$ ", so first time (i.e., right after login) you get:

hostname:~ username$ 

I’ve been trying something like in my ~/.bashrc:

function __ps1_newline_login {
  if [[ -n "${PS1_NEWLINE_LOGIN-}" ]]; then
    PS1_NEWLINE_LOGIN=true
  else
    printf '\n'
  fi
}

export PS1="\$(__ps1_newline_login)\h:\W \u\$ “

expecting to get:

# <empty line>
hostname:~ username$ 

A complete example from the the beginning would be:

hostname:~ username$ ls `# notice: no empty line desired above!`
Desktop      Documents

hostname:~ username$ 

回答1:

Try the following:

function __ps1_newline_login {
  if [[ -z "${PS1_NEWLINE_LOGIN}" ]]; then
    PS1_NEWLINE_LOGIN=true
  else
    printf '\n'
  fi
}

PROMPT_COMMAND='__ps1_newline_login'
export PS1="\h:\W \u\$ "

Explanation:

  • PROMPT_COMMAND is a special bash variable which is executed every time before the prompt is set.
  • You need to use the -z flag to check if the length of a string is 0.


回答2:

Running with dogbane's answer, you can make PROMPT_COMMAND "self-destruct", preventing the need to run a function after every command.

In your .bashrc or .bash_profile file, do

export PS1='\h:\W \u\$ '
reset_prompt () {
  PS1='\n\h:\W \u\$ '
}
PROMPT_COMMAND='(( PROMPT_CTR-- < 0 )) && { 
  unset PROMPT_COMMAND PROMPT_CTR
  reset_prompt
}'

When the file is processed, PS1 initially does not display a new-line before the prompt. However, PROMPT_CTR is immediately decremented to -1 (it is implicitly 0 before) before the prompt is shown the first time. After the first command, PROMPT_COMMAND clears itself and the counter before resetting the prompt to include the new-line. Subsequently, no PROMPT_COMMAND will execute.

Of course, there is a happy medium, where instead of PROMPT_COMMAND clearing itself, it just resets to a more ordinary function. Something like

export PS1='\h:\W \u\$ '
normal_prompt_cmd () {
   ...
}
reset_prompt () {
  PS1='\n\h:\W \u\$ '
}
PROMPT_COMMAND='(( PROMPT_CTR-- < 0 )) && {
   PROMPT_COMMAND=normal_prompt_cmd
   reset_prompt
   unset PROMPT_CTR
  }'


回答3:

2018 Update (inspired by chepner's answer)

UPDATE: Fixed PROMPT_COMMAND issues caused by other answers

Changes:

  1. No need to export PS1
  2. I used "\n$PS1" instead of re-typing.
  3. Other answers interfere with the PROMPT_COMMAND's default behavior (more info below)

Enter the following in ~/.bash_profile (substituting first line with your prompt):

PS1=YOUR_PROMPT_HERE

add_newline_to_prompt() {
  is_new_login="true"
  INIT_PROMPT_COMMAND="$PROMPT_COMMAND"
  DEFAULT_PROMPT_COMMAND=update_terminal_cwd
  PROMPT_COMMAND='{
    if [ $is_new_login = "true" ]; then
      is_new_login="false"
      eval $INIT_PROMPT_COMMAND
    else
      PS1="\n$PS1"
      PROMPT_COMMAND=$DEFAULT_PROMPT_COMMAND
    fi
  }'
}

add_newline_to_prompt

PROMPT_COMMAND

I noticed that my tab name in terminal wasn't updating to my current working directory and did some investigating. I realized that above solutions are messing with PROMPT_COMMAND. Try this out:

  1. Comment out any modifications to PROMPT_COMMAND in your config files (.bash_profile etc.)
  2. Add INIT_PROMPT_COMMAND="$PROMPT_COMMAND" to your config file

Now open a new shell:

$ echo $INIT_PROMPT_COMMAND
shell_session_history_check; update_terminal_cwd
$ echo $PROMPT_COMMAND
update_terminal_cwd

Notice that when you open a new shell, it runs both a "history check" and updates the name of the tab current working directory. Notice that it only runs the "history check" initially, and then never runs it again.

NOTE: I've only tested this on Mac's Terminal. May be different on other systems.



标签: bash shell ps1