Does anyone know how to draw lines in between the commands in zsh?

Here is an example with MobaXterm:

enter image description here

up vote 11 down vote accepted

In order to extend your current PS1 prompt by a preceding horizontal line, you can use one of the following methods. The basic principle is the same but which one works best depends on your terminal emulator, locale setting and font settings.

Print a bunch of minus (-) or underscore (_) characters

setopt promptsubst
PS1=$'${(r:$COLUMNS::_:)}'$PS1

Explanation

  • The option promptsubst enables the substitution of parameters inside the prompt each time the prompt is drawn, in this case COLUMNS.
  • The parameter expansion flag r:$COLUMNS::_: pads the right side of the parameter with underscores (given between the last two : :) until a width of $COLUMNS is reached. As in this case no parameter is given, only the padding is printed.
  • The padding takes up the whole width of the terminal, the original PS1 is automatically wrapped to the next line. So there is no need to add an extra newline. This is also important as explicit newlines may in some cases lead to the prompt overwriting the last line(s) of the output. (In my case this happened when the prompt text before the explicit newline was exactly as long as the terminal was wide.)

This works on every terminal emulator (or console), every locale and every font. But it may not look that good: At least with minuses as there will be gaps between them (----), with undercores it depends on the font. The other methods use different ways to build on that.


Underline with zsh Prompt Escapes for visual effects

setopt promptsubst
PS1=$'%U${(r:$COLUMNS:: :)}%u'$PS1

Explanation:

  • Everything between %U and %u is underlined.
  • print spaces (which will be underlined) instead of underscores

This should work with most terminal emulators, locales and fonts as it just uses underlining. A possible drawback is, that the horizontal line will not be centered but very low on the output line, just above the next line of the prompt.


Line-drawing (aka Box-drawing) using alternate character set

setopt promptsubst
PS1=$'%{\e(0%}${(r:$COLUMNS::q:)}%{\e(B%}'$PS1

Explanation:

  • %{...%} tells zsh to expect only escape codes that do not actually move the cursor
  • \e(0 switches to the alternate character set
  • q maps to a horizontal line in the alternate character set
  • \e(B switches back to the regular character set

This also should work with most terminal emulators (but probably not console), locales and fonts. The thickness of the line seems to vary between fonts and even terminal emulators using the same font (on my Machine with the Terminus font urxvt prints a thin line, while roxterm prints a very thick line).


Box-drawing using Unicode characters

setopt promptsubst
PS1=$'${(r:$COLUMNS::\u2500:)}'$PS1

Explanation

  • use the Unicode character U+2500 ("Box Drawing Light Horizontal", ) for padding.

This obviously requires the terminal emulator to support Unicode characters, a font that has the required character and an UTF-8 locale. But it also provides a few styles of line to chose from, like thick () or double lines (). (see the Official Unicode Consortium code chart for more)

  • Wow... when I was writing my answer I thought, that look too much complicated for zsh... but I haven't looked into the prompt escapes. +1 from me. – mpy Nov 28 '14 at 19:24
  • indeed this works too is much cleaner. Thanks – user52441 Nov 28 '14 at 23:35

I think the line is coded into the prompt and realized via the underline prompt escape %U:

PS1="%U                         %u
%~ "

where %~ is your usual prompt (check with print $PS1):

enter image description here

More tricky is to make the line as wide as your terminal. The number of characters is stored in $COLUMNS, so we construct a string of an appropriate number of blanks, surrounded by %U/%u:

drawline=""
for i in {1..$COLUMNS}; drawline=" $drawline"
drawline="%U${drawline}%u"

As we want to update the length if the size is altered, we redefine the prompt every time before it is redrawn, which can be accomplished by putting the code in the precmd() function:

precmd() {
   drawline=""
   for i in {1..$COLUMNS}; drawline=" $drawline"
   drawline="%U${drawline}%u"
   PS1="${drawline}
%~ "
}

Et voilà:

enter image description here

  • Thanks a lot this is exactly what I was looking for and it work fine. I just have to find how to change the line color now. – user52441 Nov 28 '14 at 8:47

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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