Adding a newline when necessary to bash's prompt
05 May 2023The 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.