3

I show my full working directory plus other information (git, etc) in my bash prompt and sometimes it gets very long.

I want to add a newline to the end of my prompt so I can type the command on the next line, but only if the prompt is long e.g. more than 50 characters.

| ~ $ Typing a command here is nice                                              |
| ~/foo/longDirectoryName/longsubirectory/src/package (master +*) $ Typing here s|
| ucks. I want to just start on a new line                                       |

Obviously, If I wanted to always type my command on the next line, I could just add a newline to PS1 (as in this post). But I haven't found a way to do that conditionally because PS1 is just a format string.


P.S. I'm actually using ZSH trying to customize the Agnoster theme but I imagine any solution for bash in general would help.

  • no matter how long or short your prompt, you're always going to have situations where the command line you're editing wraps to the next line. my way of dealing with that is to have as large a wide screen monitor as i can afford (currently 27" 1440p) with a horizontally maximised terminal (currently 231 columns x 57 lines). this makes reading log files easier too :). Also, in bash you can use ^X^E to edit the current line in $EDITOR. IIRC zsh can be configured to do the same. – cas Aug 25 at 1:28
  • I generally don't bother with fancy prompts because I rarely need the extra info in them (it's just visual clutter, and wastes valuable screen space), and get annoyed by the tiny delays incurred by running external programs to generate/format them. PS1='\u@\h:\w\$' for me :) – cas Aug 25 at 1:31
5

In zsh, that's what the %<number>(l:<yes>:<no>) prompt expansion is for. When the number is negative, like -30, if there are at least 30 characters left until the right edge of the screen, then the yes text is output, otherwise no, so:

PS1=$'%~%-30(l::\n)$ '

Would insert a newline if fewer than 28 characters (30 minus the "$ ") are left for you to use on the line.

You can do your 50 or more with:

PS1=$'%~%50(l:\n:)$ '

But IMO, it's more useful to guarantee a minimum available space, than a maximum unusable space.

See the manual for details. You'll find other directives to truncate long prompts and replace with ellipsis for instance which you may also find useful.

Note that zsh prompt expansion is completely different from that of bash. It's actually closer to that of tcsh, so solutions for bash are unlikely to be of much use for zsh, though it's generally more true the other way round.

  • This solved my problem perfectly. I ended up adding: bash newline_if_too_long() { SEGMENT_SEPARATOR=$'\ue0b0' local blue_arrow="%{%F{blue}%}$SEGMENT_SEPARATOR%{%f%}" local blue_space="%{%F{black}%}%{%K{blue}%} ↳ %{%k%}%{%f%}" echo -n "%-30(l::\n$blue_space$blue_arrow)" } as a segment to my prompt builder – B1CL0PS Aug 24 at 20:18
3

In newer versions of bash, you can combine PROMPT_COMMAND with the ${var@P} form of variable substitution ("expand as if it were a prompt").

Here is a way to do that. You simply set OPS1 instead of PS1 to the prompt you want. The \- escape inside OPS1 will act like a soft hyphen: it will expand to a newline when the prompt would be longer than COLUMNS / 2; otherwise it will be removed:

generate_ps1(){
    local ps1=${OPS1@P}
    if [ "${#ps1}" -gt "$((COLUMNS / 2))" ]; then PS1=${OPS1//\\-/$'\n'}
    else PS1=${OPS1//\\-/}
    fi
}
OPS1=$PS1
PROMPT_COMMAND=generate_ps1

# after this, set OPS1 instead of PS1 to the prompt you want

$ OPS1='$long_var\-\$ '
$ long_var=$(printf 'foobar %.0s' {0..21})
foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foo
bar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar
$ _
$ long_var=$(printf 'foobar %.0s' {0..2})
foobar foobar foobar $ _

This could be improved to act like a real soft-hyphen, to take into account the automatic margins of the terminal and the already existing newlines inside the prompt, etc; but that will make it more complex than it's worth.

  • Zsh's RPROMPT (aka RPS1) is even nicer if you setopt TRANSIENT_RPROMPT. I put the current directory, the user@host information, and the date, in RPROMPT, while PROMPT gets more dynamic information: current time, exit code of last command executed, git repo status summary/counts, etc. – sitaram Aug 25 at 14:13
  • @sitaram this answer is not about zsh and RPROMPT, but about bash, which doesn't have anything like that. Admittedly, the RPS1 name was badly chosen, I will change it ;-) – mosvy Aug 25 at 14:19
  • the original question had a PS that indicated he was "actually using zsh", so this may still be useful for him. – sitaram Aug 27 at 10:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.