11

Most of the time the output of a command ends with the newline character. But sometimes it does not, so the next shell prompt is printed in the same line together with the output.

Example:

root@hostname [~] # echo -n hello
helloroot@hostname [~] #

I've always found that very annoying.
Now, I could just add a "\n" at the beginning of the the PS1 variable, but most of the time that will print one extra line I dont need.

Is it possible to know whether the last command's output ended with a newline or not?


Solution:
(Thanks to Dennis)

PS1='$(printf "%$((`tput cols`-1))s\r")\u@\h [\w]\$ '
5
  • This should be moved to superuser.
    – raphink
    Dec 27, 2009 at 17:42
  • I like your version! You used $() in one place and backticks in another. You can use $() in both. Dec 29, 2009 at 2:08
  • I know. But for me it's easier to read that way
    – GetFree
    Dec 29, 2009 at 3:26
  • I wouldn't use tput cols because it just outputs the value of the COLUMNS variable anyway, and it's slower because it's not a shell builtin. You'll also want to include \e[K (equivalent to tput el) to delete the inserted whitespace so that you don't get a bunch of trailing whitespace when copying and pasting in the default case. Finally, you need to enclose all this magic between \[ and \] or else bash will try to second-guess your cursor position and it'll mess up when you edit your command/history.
    – dlitz
    Jun 14, 2019 at 6:35
  • 1
    The whole thing can be done as just: PS1='\[\e[7m%\e[m$( printf "%*s" "$((COLUMNS-1))" "" )\r\e[K\]\u@\h [\w]\$ '
    – dlitz
    Jun 14, 2019 at 6:35

4 Answers 4

6

I've been experimenting with the following to emulate the feature from zsh in Bash:

$ unset PROMPT_SP; for ((i = 1; i <= $COLUMNS + 52; i++ )); do PROMPT_SP+=' '; done
$ PS1='\[\e[7m%\e[m\]${PROMPT_SP: -$COLUMNS+1}\015$ '

It issues a reverse video percent sign, followed by a bunch of spaces to make it wrap to the next line, then a carriage return, followed by a dollar sign and a space. You can add prompt escapes after the "\015" to customize your prompt.

Using this depends on how your terminal handles right margin line wrapping (automatic margins). The length of PROMPT_SP is arbitrary, but should be at least 80 or whatever your usual terminal width is. You may need to hard-code that value if $COLUMNS isn't set yet by the time the for loop is run in ~/.bashrc. You may want shopt -s checkwinsize if it's not already set.

4
  • I wonder why someone downvoted each answer. Hmmm... no explanation. How helpful. Dec 28, 2009 at 0:21
  • Here's another way, without using a loop, to create the pad string: printf -v PROMPT_SP '%*s' $((COLUMNS + 52)) '' Apr 28, 2012 at 0:28
  • What is a "reverse video percent sign"? The word "video" has me confused, and I haven't been able to find the answer on Google. Nov 22, 2013 at 23:29
  • 1
    @davidchambers: The background of the character is displayed in the foreground color and the character itself is displayed in the background color. See man 5 terminfo and search for "reverse video" to see some documentation that uses this terminology. Nov 22, 2013 at 23:56
1

zsh tries to solve your problem. If the last output ends without a newline, you will get:

$ echo -n 'abc'
abc%
$ 

Where the % uses inverted background/foreground. Not sure if it's portable to bash in any way.

0

No it isn't possible. Bash itself does not process or see the output of the program it has started.

It just occured to me that it might be possible to write a program to set PROMPT_COMMAND to, which would check the current position of the cursor and issue a newline if the cursor was not at the left edge.

1
  • Good idea. The only problem is... is it possible to know the cursor position?
    – GetFree
    Dec 27, 2009 at 3:28
0

I got this idea from @Teddy of detecting the horizontal cursor position and issuing a newline if it isn't at the first column.

In bash, we can detect the cursor position using this command: IFS=';' read -sdR -p $'\E[6n' ROW COL. More information in this answer: https://unix.stackexchange.com/a/183121/340297
The COL variable holds the current column where the cursor is. If $COL is not equal to 1, add a newline before setting the PS1 variable.

Here is my implementation:

_set_prompt() {
    IFS=';' read -sdR -p $'\E[6n' ROW COL # Get cursor position
    [ $COL -ne 1 ] && echo '' # Add newline if cursor is not at 1st column
    PS1="........." # Your PS1 variable
}

PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND; }'_set_prompt'

And here is the result (with different PS1):
enter image description here

echo -n doesn't include newline, but newline is issued before displaying the prompt. Whereas, there is no extra newline if newline is already printed by the normal echo command.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .