Bof.

No theme, no regular posting.

Archive

© 2014-2023. Raphaël Rigo CC-BY-SA 4.0

About.

Adding a newline when necessary to bash's prompt

The problem

Since years I’ve raged when my cursor in my bash prompt was incoherent: the blinking rectangle was in one place but deleting chars would delete the wrong ones.

I finally took the time to try to understand what was wrong. Long story short: if the previous command did no end with a newline.

A typical case is when using curl, which by default, outputs the page without adding a newline at the end. But a simple way to reproduce is to use echo:

trou@valhalla:~$ echo -n test
testtrou@valhalla:~$

But the problem is that it’s not bash’s role to check the output of the last command. Some “modern” shells such as zsh or fish actually do that. But I wanted to find a solution which works everywhere, and bash is the common denominator for most Linux and Unix systems.

How to fix?

I first found a solution which involved writing spaces until the line wraps, but it broke when using a python virtualenv.

The answer on this StackExchange question is actually quite clever: is reads the current cursor position using terminal escape codes and only outputs a newline if we are not in the first column.

The final result for me is:

PS1='${debian_chroot:+($debian_chroot)}\[\e[38;5;111m\]\u@\[\e[92m\]\h\[\e[39m\]$text:\[\e[38;5;86m\]\w\[\e[00m\]\$ '

# https://github.com/dylanaraps/pure-bash-bible#get-the-current-cursor-position
new_line_ps1() {
  local _ y x _
  local RESET="\001\e[0m\002"

  IFS='[;' read -p $'\e[6n' -d R -rs _ y x _
  if [[ "$x" != 1 ]]; then
    printf "\n${RESET}"
  fi
}

Midnight Commander

One last thing to fix is to avoid mc weird behaviour (slooooow start) when running with this PS1, by actually disabling the functionnality when running under mc:

# Don't add newline in mc
if [ -z $MC_SID ]; then
    PS1="\$(new_line_ps1)$PS1"
else
    PS1="[mc]$PS1"
fi

Fish also had a problem, which they workedaround with the exact same if.