Customizing the Terminal: The Prompt

Terminal

Most Linux ‘gurus’ spend a lot of time working in the terminal. If you belong to that group, this post is for you. This is a tutorial to configure the terminal prompt to the best possible value for your use. Note: This tutorial is for bash users – these instructions will not work in other shells.

The Prompt

You must have seen the prompt if you have use the terminal – it is the first few characters in each line. Usually, it will be…

[username@localhost] ~ $

In this case, the user is shown three piece of information in the prompt –

  • Username of the current user
  • Hostname
  • Current folder name

This post will show you how to customize this prompt to your needs.

Editing the Prompt

Editing the prompt is very simple – you just have to edit a shell variable. To see the current prompt’s value, open a shell and type the command…

echo $PS1

The result will be something like this(in Ubuntu)…

binnyva@binlap:~$ echo $PS1
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$

Which is functionally the same as…

\u@\h:\W\$ 

To edit this variable, run the command…

export PS1=<New Prompt Value>

Most desktop systems don’t need the username and hostname in the prompt – this is only relevent if your are connected to a remote system. So the first thing to do, if you are on a desktop system, is to remove those two. To do that, run the command…

export PS1="[\W]\$ "

This will change the prompt in the current terminal. To make it permanent, edit the ~/.bashrc and set the PS1 variable there. Just add this line at the end of the file…

export PS1="[\W]\$ "

A Better Prompt

Currently, the prompt has the basename of the current working directory. That is, if we are in ‘~/Sites/Lindesk/posts’, the prompt will be ‘[posts]$ ‘. This is good enough for most people. But I have a problem with this. If I go to another folder, say, ‘~/Sites/OpenJS/posts’, the prompt is still ‘[posts]$ ‘. The prompt is a bit ambiguous in this case. This can be done using a different character – in this case \w(small ‘w’ – the default was capital ‘W’).

[posts]$ export PS1="[\w]$ "
[~/Sites/OpenJS/posts]$ _

This is nice – but you will have a problem if the directory you are in is several levels deep. It might be something like this…

[/var/www/html/sites/Lindesk/lindesk.com/wp-content/plugins/eventr/langs]$ _

That’s long – and inconvenient. There are better ways of doing this.

Show the Beginning and the End.

A better way of doing this is to cut of a part of the folder – so the above path will look something like…

[/var/www/html.../eventr/langs] $ _

This option will show the first 15 characters of the path and then the last 15 characters – if the directory path is bigger than 30 characters. To enable this mode, open up the file ~/.bashrc and add this code…

PROMPT_COMMAND='DIR=`pwd|sed -e "s!$HOME!~!"`; if [ ${#DIR} -gt 30 ]; then CurDir=${DIR:0:12}...${DIR:${#DIR}-15}; else CurDir=$DIR; fi'
PS1="[\$CurDir] \$ "

The First Character of Each Directory

There is yet another method – I got this idea from the fish shell. In this approach, the big path will appear as…

[/v/w/h/s/L/l/w/p/e/langs] $ _

In this option, only the first character of each parent folder will be shown. Only the base folder name will be shown entirely. This is the approach I use. If you want to use this, open the ~/.bashrc file and add this…

PROMPT_COMMAND='CurDir=`pwd|sed -e "s!$HOME!~!"|sed -re "s!([^/])[^/]+/!\1/!g"`'
PS1="[\$CurDir] \$ "

Prompt Variables

The other values you can insert into the prompt are…

\d
the date in “Weekday Month Date” format (e.g., “Tue May 26”)
\D{format}
the format is passed to strftime(3) and the result is inserted into the prompt string; an empty format results in a locale-specific time representation. The braces are required
\e
an ASCII escape character (033)
\h
the hostname up to the first ‘.’
\H
the hostname
\j
the number of jobs currently managed by the shell
\l
the basename of the shell’s terminal device name
\n
newline
\r
carriage return
\s
the name of the shell, the basename of $0 (the portion following the final slash)
\t
the current time in 24-hour HH:MM:SS format
\T
the current time in 12-hour HH:MM:SS format
\@
the current time in 12-hour am/pm format
\A
the current time in 24-hour HH:MM format
\u
the username of the current user
\v
the version of bash (e.g., 2.00)
\V
the release of bash, version + patch level (e.g., 2.00.0)
\w
the current working directory, with $HOME abbreviated with a tilde
\W
the basename of the current working directory, with $HOME abbreviated with a tilde
\!
the history number of this command
\#
the command number of this command
\$
if the effective UID is 0, a #, otherwise a $
\nnn
the character corresponding to the octal number nnn
\\
a backslash
\[
begin a sequence of non-printing characters, which could be used to embed a terminal control sequence into the prompt
\]
end a sequence of non-printing characters

22 comments

  1. For some reason the script to truncate the directory length does not work from me from within bashrc. If I issue it at the terminal itself it works. I’ve spent several hours trying to figure this out and making changes to no avail. If it helps any I’m using Ubuntu 8.10 and the terminal I’m in is in fact bash.

  2. wyn

    did you use export?

    in any shell script you have to “export” your variables, so mine reads:

    –[cut]–
    export PROMPT_COMMAND=’DIR=`pwd|sed -e “s!$HOME!~!”`; if [ ${#DIR} -gt 30 ]; then CurDir=${DIR:0:12}…${DIR:${#DIR}-15}; else CurDir=$DIR; fi’
    PS1=”[\$CurDir] \$ ”
    –[/cut]–

    excuse the line wrapping, but that should work.

  3. @Wyn
    I have the same issue. So far I’ve gotten the command to give the correct value for $CurDir, but it refuses to update. I fixed the command by replacing “!” chars with “|” chars in the sed script:

    DIR=`pwd|sed -e “s|$HOME|~|”`

    Still trying to get the update to work.

  4. Ok. Figured it out. The default Ubuntu (& probaly Debian) .bashrc sets PROMPT_COMMAND later in the script to update the title of xterms. So this was overriding the new one. I combined them so that both happened. Here’s my new stuff:

    PROMPT_COMMAND=’DIR=${PWD/$HOME/~}; if [ ${#DIR} -gt 30 ]; then CurDir=${DIR:0:12}…${DIR:${#DIR}-15}; else CurDir=$DIR; fi; export CurDir’
    PS1=”\[33[01;32m\]\u\[33[00m\]@\[33[01;32m\]\h\[33[00m\]:\[33[01;36m\]\$CurDir\[33[00m\]\$ ”

    That PS1 includes colors, which I like.

    Then:

    case “$TERM” in
    xterm*|rxvt*)
    PC2=’echo -ne “33]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}07″‘
    PROMPT_COMMAND=”$PROMPT_COMMAND;$PC2”
    ;;
    *)
    ;;
    esac

    Note: The export mentioned by @ejes above isn’t necessary.

    Hope this helps.

  5. So some of my last post was garbled. Also you should put quotes around the “${PWD/$HOME/~}” thing to get that to work. Just seemed easier than piping ‘pwd’ through ‘sed’.

    Thanks again for the tip!

  6. aaaah that was it Paul, and yeah I had switched to piping myself in the DIR= statement, I didn’t bother mentioning it since nothing was working yet. Once I fixed that second Prompt_Command everything works beautifully, tyvm. Now I wonder if there’s a way to rig the output of pwd to put a \ before spaces in folder names (for the windows shares on my network)….

  7. Oh, and for what it’s worth, my PS1 is a combination of colors, multilines, and this truncation:
    PS1=’\n\[33[1;31m\] ${debian_chroot:+($debian_chroot)}\[33[1;31m\]\u@\h\[33[00m\]: \[33[01;34m\] $CurDir \[33[00m\]\n\[33[1;31m\] \t $ \[33[0;39m\]’

  8. Nice article.
    I hate one-line hacks.
    Here’s an improvement:

    _update_prompt () {
    DIR=`pwd|sed -e “s!$HOME!~!”`
    if [ ${#DIR} -gt 30 ]; then
    CurDir=${DIR:0:12}…${DIR:${#DIR}-15}
    else
    CurDir=$DIR
    fi
    }
    PROMPT_COMMAND=_update_prompt
    PS1=”[\$CurDir] \$ ”

    Also…

    @ejes PROMPT_COMMAND does not need to be exported.

    @wyn Make sure your ~/.profile or ~/.bash_profile sources ~/.bashrc. From Debian’s default .profile:

    # if running bash
    if [ -n “$BASH_VERSION” ]; then
    # include .bashrc if it exists
    if [ -f “$HOME/.bashrc” ]; then
    . “$HOME/.bashrc”
    fi
    fi

  9. Pingback: Links of the Week
  10. Oops. I tried the last approach (which you use) and got this error on my Mac, running OS X 10.5.7

    sed: illegal option — r
    usage: sed script [-Ealn] [-i extension] [file …]
    sed [-Ealn] [-i extension] [-e script] … [-f script_file] … [file …]

  11. Hmmm… a quick man sed and a guess…
    changed -re to -E and seems to work now…

    PROMPT_COMMAND=’CurDir=`pwd|sed -e “s!$HOME!~!”|sed -E “s!([^/])[^/]+/!\1/!g”`’
    PS1=”[\$CurDir] \$ ”

    nice.

  12. hi, when I use the command:

    export PS1=”\e[0;31m[\u@\h \W]\$ \e[m ”

    I am unable to add lines while I am writing code. for example

    when I am writing a script on command line, it does not add a new line, it simply overrides the prompt line.
    ex:
    PROMPT $

    | grep “>” | PROMPT$ cat namefile.txt |

    is there a way around it?

  13. @newbie, You need to surround any terminal escape sequences with \[ and \], i.e., do this:

    export PS1=”\[\e[0;31m\][\u@\h \W]\$ \[\e[m\] ”

    The PROMPTING section of bash(1) documents this.

    You may also wish to read tput(1) and terminfo(5) if you want to be terminal-agnostic.

  14. I am only able to change the PS1 prompt for each session, when I logout it’s back to original prompt. How do I change it perrmanently?

    Of less importance, I’d prefer home directory not to be abbreviated as ~ tilde. Anyway to do that? Thanks.

  15. Edit ~/.bashrc. Make sure your ~/.bash_profile sources ~/.bashrc (most Linux distros preconfigure that for you these days) so interactive login shells get that setting too.

    For unabbreviated directory paths in the prompt:

    PS1=’$PWD \$ ‘ # e.g.

    You must use single quotes there.

Leave a Reply

Your email address will not be published. Required fields are marked *