Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I am sure that I have seen someone have a part of their prompt aligned to the right in their terminal window and then have the actual cursor start on a second line. I know that I can achieve the second line with a "\n" in the PS1, but I cannot figure out how to align part of it to the right. Was what I saw just whitespace added between the two strings?

share|improve this question
add comment

5 Answers

up vote 8 down vote accepted

What you want can fairly easily be done by displaying the first line before displaying the prompt. For example, the following displays a prompt of \w on the left of the first line and a prompt of \u@\h on the right of the first line. It makes use of the $COLUMNS variable which contains the width of the terminal and the $PROMPT_COMMAND parameter which is evaluated before bash displays the prompt.

print_pre_prompt () 
{ 
    PS1L=$PWD
    if [[ $PS1L/ = "$HOME"/* ]]; then PS1L=\~${PS1L#$HOME}; fi
    PS1R=$USER@$HOSTNAME
    printf "%s%$(($COLUMNS-${#PS1L}))s" "$PS1L" "$PS1R"
}
PROMPT_COMMAND=print_pre_prompt
share|improve this answer
    
Note that things get significantly more complicated if you want a colored left prompt, since the non-printing characters mean that string length is not the same as the number of characters displayed. –  Mu Mind Sep 1 '12 at 6:49
add comment

Based on the information I found here I was able to discover a simpler solution to right align while accommodating variable length content on the right or left including support for colour. Added here for your convenience...

Note on colours: using the \033 escape in favour of alternatives, without \[\] groupings, proves most compatible and therefor recommended.

The trick is to write the right hand side first, then use carriage return (\r) to return to start of line and continue to overwrite the left hand side content on top of that, as follows:

prompt() {
    PS1=$(printf "%*s\r%s\n\$ " "$(tput cols)" 'right' 'left')
}
PROMPT_COMMAND=prompt

I am using tput cols on Mac OS X to retrieve the terminal/console width from terminfo since my $COLUMNS var is not populated in env but you may substitute the replaceable "*" value in %*s, by providing "${COLUMNS}", or any other value you prefer, instead.

The next example uses $RANDOM to generate different length content includes colours and shows how you might extract functions to refactor the implementation to reusable functions.

function prompt_right() {
  echo -e "\033[0;36m$(echo ${RANDOM})\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m${RANDOM}\033[0m"
}

function prompt() {
    compensate=11
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

Since printf assumes the length of string to be the # of characters we need to compensate for the amount of characters required to render the colours, you will find it always short of the end of screen because of the non printed ANSI characters without compensation. The characters required for colour remains constant and you will find that also printf takes into account the change in length, as returned by $RANDOM for example', which keeps our right alignment in tact.

This is not the case with special bash prompt escape sequences (ie. \u, \w, \h, \t) though, as these will only record a length of 2 because bash will only translate them when the prompt is displayed, after printf has rendered the string. This does not affect the left hand side but best to avoid them on the right.

Of no consequence if the generated content will remain at constant length though. Like with the time \t option which will always render the same amount of characters (8) for 24 time. We only need to factor in the compensation required to accommodate for the difference between 2 characters counted which results to 8 characters when printed, in these cases.

Keep in mind that you may need to triple escape \\\ some escape sequences which otherwise hold meaning to strings. As with the following example the current working directory escape \w holds no meaning otherwise so it works as expected but the time \t, which means a tab character, does not work as expected without triple escaping it first.

function prompt_right() {
  echo -e "\033[0;36m\\\t\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m\w\033[0m"
}

function prompt() {
    compensate=5
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

nJoy!

share|improve this answer
add comment

Using printf with $COLUMNS worked really well, something like:

printf "%${COLUMNS}s\n" "hello"

It right justified it perfectly for me.

share|improve this answer
add comment

You can use printf to do right alignment:

$ printf "%10s\n" "hello"
     hello

$ PS1='$(printf "%10s" "$somevar")\w\$ '
share|improve this answer
add comment

Adding on Giles' answer, I wrote something to handle colors better (provided they're properly enclosed in \[ and \]. It's case-by-case and doesn't handle every case, but it lets me set my PS1L in the same syntax as PS1 and uses the (uncolored) date as PS1R.

function title {
    case "$TERM" in
    xterm*|rxvt*)
        echo -en "\033]2;$1\007"
        ;;
    *)
        ;;
    esac
}

print_pre_prompt() {
    PS1R=$(date)
    PS1L_exp="${PS1L//\\u/$USER}"
    PS1L_exp="${PS1L_exp//\\h/$HOSTNAME}"
    SHORT_PWD=${PWD/$HOME/~}
    PS1L_exp="${PS1L_exp//\\w/$SHORT_PWD}"
    PS1L_clean="$(sed -r 's:\\\[([^\\]|\\[^]])*\\\]::g' <<<$PS1L_exp)"
    PS1L_exp=${PS1L_exp//\\\[/}
    PS1L_exp=${PS1L_exp//\\\]/}
    PS1L_exp=$(eval echo '"'$PS1L_exp'"')
    PS1L_clean=$(eval echo -e $PS1L_clean)
    title $PS1L_clean
    printf "%b%$(($COLUMNS-${#PS1L_clean}))b\n" "$PS1L_exp" "$PS1R"
}

Here it is on github: dbarnett/dotfiles/right_prompt.sh. I use it in my .bashrc like this:

source $HOME/dotfiles/right_prompt.sh
PS1L='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]'
PS1='\[\033[01;34m\]\w\[\033[00m\]\$ '
PROMPT_COMMAND=print_pre_prompt

Note: I also added a newline after PS1R, which makes no visual difference, but seems to keep the prompt from getting garbled if you scroll back through certain commands in your command history.

I'm sure someone else can improve on this, and maybe generalize some of the special-case-iness.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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