Forcing a new line on a bash prompt based on last command

2018-05-08

I can't believe it's taken this long to work this out.

I use bash almost exclusively, for my sins I've never gotten along with zsh or other fancier shells (I went through a phase of using fish, but it didn't last).

One of the biggest irritants I've put up with for years now, is when you cat a text file without a finishing line break / carridge return / EOF whatever. Your prompt would be appended to the same line.

Most of the time, I'd just do "cat whatever; echo ''" or something. But I've finally worked out a good(ish) solution.

This is all done in the .bashrc:

## Check if we are on the start of a new line
new_line_break () {
	exec < /dev/tty
	oldstty=$(stty -g)
	stty raw -echo min 0
	echo -en "\033[6n" > /dev/tty
	read -sdR CURPOS
	stty $oldstty
	CURPOS=${CURPOS#*[}
	CURPOS=(${CURPOS//;/ })
	(( ${CURPOS[1]} > 1 )) && printf "\n"
}

export PROMPT_COMMAND="new_line_break"
export PS1="[\w]% "

This is a hack of half a dozen different methods I've stumbled across on the web trying to find a solution but what it does.

Calling the function new_line_break from the bash variable PROMPT_COMMAND, it checks if the current cursor X position is greater than 1, and if it is, it prints a new line. PS1 is then executed.

I've honestly never really used /dev/tty, but it's the only way I managed to get the cursor position in a nested function to work, otherwise it would just hang.

The closest to this was the accepted answer here: https://serverfault.com/questions/97503/bash-how-to-know-if-the-last-commands-output-ends-with-a-newline-or-not. However, this is heavily reliant on the terminal emulator not being poked and pulled (if you change window width, you're likely to mess up the text display).

I haven't found a way of breaking this yet and though it does restrict the prompt slightly more than default, I can't think of a use case where this matters (moving the cursor around in a script still works).

Also, I know it's been over a year... I do at least have a bunch of new scripts I will be sharing.