Is Your Bash Prompt Cramping Your Style?

It is sometimes hard to form a mental model of a directory tree when working with the command line. GUI shells tend to provide more visual cues. For example, in OS X, the Finder has a column view that allows you to quickly see how directories are nested.

Finder column view showing nested directories

So, like a lot of Bash users, I used to get around this by printing the current working directory in my shell prompt:

Directory structure printed in Bash prompt

However, things can get pretty cramped when dealing with deeply nested directories. A better solution is needed. Ideally, I want a minimal prompt that just displays my user@host. I only need to get my bearings when I’m moving around in the directory tree. Therefore, I’ve come up with the following.

Directory structure printed after every cd.  Bash prompt no longer prints current working directory.

The current working directory is printed after every cd. This allows me to get my bearings when I need to, while keeping my Bash prompt short and sweet. The following incantation in ~/.bashrc does the trick:

# Print working directory after a cd.
cd() {
    if [[ $@ == '-' ]]; then
        builtin cd "$@" > /dev/null  # We'll handle pwd.
    else
        builtin cd "$@"
    fi
    echo -e "   \033[1;30m"`pwd`"\033[0m"
}

Have you come up with other solutions for longpromptitis? Let me know in the comments!

 
 
27 Comments. Leave a comment or send a Trackback.
  1. Pingback
    Trackback: A Clean Bash prompt « Init • December 3 2010

    [...] however it wasn’t complete for people in the default Ubuntu Linux environment. The article, here, shows that by adding this amount of code to your ~/.bashrc file you can modify your prompt: # [...]

  1. #1 • chvnx said on November 24 2010:
     

    Woooah, slow down. You post far to often.

    ;)

  2. #2 • Clinton Curry said on November 24 2010:
     

    In ZSH, there’s an environment variable called $RPS1 which gives the right aligned prompt text. My specific settings are:

    PS1=’%B(%h) %m%#%b ‘
    RPS1=’(%20<…<%~)'

    and here is how they appear.

    http://imgur.com/OAZhC

  3. #3 • Shane said on November 24 2010:
     

    @Clinton: I must say that ZSH handles this much better than Bash does.

  4. #4 • Christian Witts said on November 24 2010:
     

    I simply set PS1=”[\t][\u@\H:\w]\r\n$ ” which gives me a prompt looking like
    [16:02:33][chrisw@arch:/mnt/Storage/workspace]
    $

  5. #5 • Kevin Canini said on November 24 2010:
     

    Can I ask what terminal program you’re running, and how you got it to appear like that? Specifically, the info bar at the bottom and the color scheme.

  6. #6 • Shane said on November 24 2010:
     

    Kevin, I’m using Terminal.app with a SIMBL plugin called terminalcolours to customise the colours. The theme is a modified version of ir_black.

    The bar at the bottom is a tmux status bar.

  7. #7 • Kevin Canini said on November 24 2010:
     

    Awesome, thanks for the info and the links!

  8. #8 • Wainamoinen said on November 25 2010:
     

    I use a very simple prompt. Just the hostname up to the first dot, and the last part of the path (up to the last slash).

    PS1=’[\h] \W> ‘

    It looks like: [compute-0-29] log>

    [compute-0-29] log> hostname
    compute-0-29.local
    [compute-0-29] log> pwd
    /var/log

    I use this because I usually don’t really need to know all the hostname nor the full path.

  9. #9 • rrspurlock said on November 25 2010:
     

    Agreed. I like having the following displayed:

    1. Username (to make sure I’m in the correct context)
    2. Complete, current work directory path
    3. Hostname (to make sure I’m issuing on the right host in this terminal)
    4. Most importantly… a consistent cursor position for command entry

    export PS1=”[\u:\w]\n\h:”

    This looks like…

    [theUserName:/data/subdir/subdir2/subdir3]
    hostname:

    The cursor is placed after the colon. Independent of the working directory depth, the command is issued at the same location.

  10. #10 • Steve Simmons said on November 25 2010:
     

    Add this command to your .bashrc:

    proml () 
    { 
        case $TERM in 
            xterm*)
                PS1="\\[\33]0;\\u@\\h   \\w\0733[7m\\]: \\u@\\h {\${HISTCMD}} \$;\\[33[m\\] "
            ;;
            *)
                PS1="\\[33[7m\\]: \\u@\\h {\\!} \$;\\[33[m\\] "
            ;;
        esac;
        export PS1
    }

    And your prompt will look like this:

    : scs@phred {321} $; 

    but the banner at the top of your terminal window will be painted with the path (among other things). Most of the explanation of those things will be left to the student, but one of the non-obvious features is that a prompt like that can be cut and pasted along with any trailing command, and only the trailing command will be executed. The prompt will be ignored when pasted.

    I recommend the Bash Prompt Howto page at http://tldp.org/HOWTO/Bash-Prompt-HOWTO/, especially section 6.3 on how to set the banner bars.

  11. #11 • Stef said on November 25 2010:
     

    I use a two lines prompt and I also update the title of the xterm window after each command.

    The prompt looks like that

    @ [user@host] /a/very/long/path/to/something/
    @ 

    where @ is a blank character with a colored background. In the example below, that color is controlled by the col2 escape sequence which uses the 256 color extension of XTerm for a more pleasing look. Most modern terminal emulators support 256 colors but the linux console (TERM=console) does not.

    Also, I recompute the prompt after each command using PROMPT_COMMAND. This is not stricly required but that potentially allows more complex customizations

    ================

    export PROMPT_COMMAND='set-xterm-title ; set-prompt' 
    
    XTERM_TITLE="Term"
    
    function set-xterm-title () {
        local title
        title=$PWD
        title=${title//#$HOME/\~}/
        if [ -n "$XTERM_TITLE" ] ; then
           title="$XTERM_TITLE [$title]" 
        fi
        echo -ne "33]0;$title07"
    }
    
    function set-prompt () {
       local col0="\[\e[0m\]"     # reset 
       local col1="\[\e[1;31m\]"  # pwd color 
       local col2="\[\e[48;5;$((16+36*2+6*2+6))m\]"  # left margin color
       local col3="\[\e[1;33m\]"  # [user@host] color
       PS1="\n$col2 $col0 $col3[\u@\h] $col1\w$col0 \n$col2 $col0 "
    }
  12. #12 • Shane said on November 25 2010:
     

    @Steve Simmons: That’s a neat idea, having a prompt that is ignored when the whole line is copied and pasted.

  13. #13 • Stef said on November 25 2010:
     

    Hoops! The formating messed up my commands. It is unfortunately not possible to test before posting but I hope this works better:

    export PROMPT_COMMAND="set-xterm-title ; set-prompt"
    
    XTERM_TITLE="Term"
    
    function set-xterm-title () {
      local title
      title=$PWD
      title=${title//#$HOME/\~}/
      if [ -n "$XTERM_TITLE" ] ; then
      title="$XTERM_TITLE [$title]"
      fi
      echo -ne "33]0;$title07"
    }
    
    function set-prompt () {
      local col0="\[\e[0m\]" # reset
      local col1="\[\e[1;31m\]" # pwd color
      local col2="\[\e[48;5;$((16+36*2+6*2+6))m\]" # left margin color
      local col3="\[\e[1;33m\]" # [user@host] color
      PS1="\n$col2 $col0 $col3[\u@\h] $col1\w$col0 \n$col2 $col0 "
    }
    
  14. #14 • Gwh said on November 25 2010:
     

    Actually I’d think that the moment I use ‘cd’ is also the moment I least need to know $(pwd). I have that information in PS1 so that when I come back to a shell I immediately know where I am. If I use ‘cd’ I already know where I’m going.

  15. #15 • mina86 said on November 25 2010:
     

    I feel the same as Gwh. Personally I use a truncated pwd so that when it gets too long only the end of it is shown. As a matter of fact, for this purpose I’ve created a shell scirpt () which shows only the end of the pwd. I use it as „PS1=’$(tpwd -n 30 {)\$ ‘”. This is similar to the way ZSH’s “%20<…<%~” works shown by Clinton Curry.

    Also, I've never felt the need for my prompt be ignored where pasting like Steve Simmons' is even though my PS2 is „:; ”. ;) Still, even this I rarely care about.

    In my opinion, there are far better ways to improve the prompt. For instance, as a programmer I find it invaluable that my prompt prints the name of a checked out git branch if I happen to be in a git repository. It also changes username's colour if I'm root, hostname's colour when I'm connected via SSH, and the trailing „$” when the previous command finished unsuccessfully. Those visual indicators proven to be rather handy. My whole prompt can be found at .

    Any way, the prompt is a matter of taste, so everyone who uses shell all the time should spend some time customising it.

  16. #16 • warnaud said on November 25 2010:
     

    I use this trick: http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html

  17. #17 • Danny Navarro said on November 26 2010:
     

    I recently blogged about the customizations I made to my ZSH prompt. I split the prompt in 2 lines.

    http://blog.dannynavarro.net/2010/10/16/zsh-prompt-for-virtualenv-git-and-bzr/

    In the post I don’t show that in the case the path line gets too long, the path gets truncated from the left side. That’s usually is enough to give enough context.

  18. #18 • dnebdal said on November 26 2010:
     

    I’ve gone for a simpler hostname + innermost two directories; that seems to be enough to remember where I am. In tcsh:
    set prompt = ‘%m %B%C2%b > ‘
    (%m is hostname, %B and %b turn on and off bold, and %Cn is the n innermost levels of the path. )

    I do like the two-line idea, though I also value compactness (it makes a small embedded terminal in e.g. kate more useful)… hm. I’ll have to test it at some point.

  19. #19 • Cougar said on November 27 2010:
     

    Here are some ideas what to do with your prompt

    http://matt.might.net/articles/console-hacks-exploiting-frequency/

    I use PROMPT_COMMAND so that after each directory change it displays new directory only once together with last seven modified filename like this:

    lost> cd /
    /: tmp etc root dev sbin bin lib
    lost> cd /tmp
    /tmp: vnc.err orbit-cougar plugtmp-159 FlashXXhNLOze cvs3cz4lF~ plugtmp-158 svi69.tmp
    lost> cd
    /home/cougar: Mail Download dead.letter zx1 zx2 test calendar.vcf

  20. #20 • Zeroth said on November 30 2010:
     

    I wrote a script (I call it ‘sd’ for Stored Directory”) which let’s me assign number, letters, or strings to directories. by typing sd 1 `pwd` it stores the current directory at “1″, and to cd to this dir all I have to type from now on is ‘sd 1′

    I like this a whole lot better than that push/pop dir thing people always talk about. and also sd stored the directories in a temp file in your home directory, so it works across multiple shells and after reboots and such.

    #!/bin/bash
    
    sdfile="$HOME/.stored-paths"
    
    function delete_entry {
            newfile=""
            firstline=1;
            while read line; do
                    i=`echo $line | cut -d\  -f1`
                    if [[ "$i" != "$1" ]] ; then
                            if [[ "$firstline" == "0" ]] ; then
                                    newfile="$newfile"$'\n'
                            else
                                    firstline=0
                            fi
                            newfile="$newfile""$line"
                    fi
            done  $sdfile
    #       eval echo "\${newfile%\$'\n'}" #> #sdfile
    }
    
    function store_entry {
            while read line; do
                    i=`echo $line | cut -d\  -f1`
                    if [[ "$i" == "$1" ]] ; then
                            delete_entry "$i"
                            break
                    fi
            done > $sdfile
            sort < $sdfile -o $sdfile
    }
    
    function change_dir {
            while read line; do
                    i=`echo $line | cut -d\  -f1`
                    if [[ "$1" == "$i" ]] ; then
                            dir="`echo $line | cut -f 2 -d\ `"
                            cd "$dir"
                            echo "$dir"
                            return 0
                    fi
            done &2
            return 1
    }
    
    if [[ -f $sdfile ]]; then
            echo > /dev/null
    else
            touch $sdfile
    fi
    
    if [[ "$1" == "--clear" ]] ; then
            printf "" > $sdfile
    elif (( $# == 0 )) ; then
            cat "$sdfile"
    elif [[ "$1" == "-d" ]] ; then
            delete_entry "$2"
    elif [[ "$2" != "" ]] ; then
            store_entry "$1" "$2"
    elif [[ "$1" != "" ]] ; then
            change_dir "$1"
            return "$?"
    elif "$1" == "-h" || "$1" == "--help" ; then
            echo "sd - store directories for quick cd'ing"
            echo "Copyright (C) Brandon Captain. Released under the GPL v2."
            echo ""
            echo "Usage: sd 1 /usr/src/linux/  # store path at identifier '1'"
            echo "Usage: sd bin /usr/bin       # store path at identifier 'bin'"
            echo "Usage: sd 1                  # cd to path at identifier '1'"
            echo "Usage: sd                    # show all paths"
            echo "Usage: sd -d 1               # clear path 1"
            echo "Usage: sd --clear            # clear all paths"
            echo "Usage: sd -h                 # this help screen"
            echo "Usage: sd --help             # this help screen"
    fi
  21. #21 • invaderzim said on November 30 2010:
     

    Would you be willing to post that snazzy tmux config?

    Thanks for the great post!

  22. #22 • Shane said on November 30 2010:
     

    @invaderzim: Sure, here’s my .tmux.conf:

    # Convenient way to reload config file.
    bind-key r source-file ~/.tmux.conf
    
    # We're used to GNU Screen key bindings.
    set-option -g prefix C-a
    unbind-key C-b
    bind-key C-a last-window
    bind-key a send-prefix
    
    # Start window numbers at 1.
    set-option -g base-index 1
    
    # Use Vi mode.
    set-window-option -g mode-keys vi
    set-option -g status-keys vi
    
    # Splitting windows into panes.
    bind v split-window -h
    bind s split-window -v
    
    # Selecting panes.
    #
    # Following are not yet supported in our version (1.2) of tmux.
    #bind h select-pane -L
    #bind j select-pane -D
    #bind k select-pane -U
    #bind l select-pane -R
    
    # Use these to select panes for now.
    bind h up-pane
    bind j down-pane
    bind k up-pane
    bind l down-pane
    
    # Resizing panes.
    bind-key -r C-h resize-pane -L
    bind-key -r C-j resize-pane -D
    bind-key -r C-k resize-pane -U
    bind-key -r C-l resize-pane -R
    set-option -g repeat-time 700
    
    # Mouse support.
    setw -g mode-mouse on
    set-option -g mouse-select-pane on
    
    #setw -g monitor-activity on
    set-window-option -g automatic-rename on
    set-option -g set-titles on
    set-option -g set-titles-string '#H'
    
    # Customise status bar.
    set-option -g status-bg black
    set-option -g status-fg white
    set-window-option -g window-status-current-bg yellow
    set-window-option -g window-status-current-fg black
    set-window-option -g window-status-current-format ' #I #W '
    set-window-option -g window-status-format ' #I #W '
    set-option -g status-left ' '
    set-option -g status-justify left
    set-option -g status-right ' #[fg=yellow]#T '  # Show terminal title (I pass pwd to the terminal title).
    set-option -g status-right-length 150
    
    # Customise pane border colours.
    set-option -g pane-active-border-fg black
    set-option -g pane-active-border-bg default
    set-option -g pane-border-fg black
    set-option -g pane-border-bg default
  23. #24 • Tom said on December 16 2010:
     

    The only thing I do differently is add an “ls” in there. I see the directory contents every time I change directories.

  24. #25 • Celeste Gonzalez said on December 24 2010:
     

    [...] however it wasn’t complete for people in the default Ubuntu Linux environment. The article, here, shows that by adding this amount of code to your ~/.bashrc file you can modify your prompt: # [...]

  25. #26 • Bacus said on April 7 2011:
     

    Am I the only one missing bash’s TAB completion after sourcing your cd() function?

  26. #27 • Shane said on April 8 2011:
     

    Bacus, tab completion still works for me.

 

Comment: