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 normally have just the name of the current directory in my bash prompt (PS1='\u@\h:\W$ '), so if I am in ~/projects/superapp/src/ I get:

hamish@host:src$ 

However I'd like to have an indication of the full path without having the full path. I've seen screenshots where people would have

hamish@host:~/p/s/src$ 

if in the example directory above. So what value of PS1 would give that? Or failing that, what script do I need in my .bashrc to produce that?

share|improve this question
1  
This is a great question, but it's also an exact duplicate of a question (already answered) on Stack Overflow: stackoverflow.com/questions/1616678/bash-pwd-shortening –  Telemachus Aug 24 '10 at 21:40
3  
It's not actually an exact duplicate, though it is similar. But looking through that I can't easily see exactly how to do what I want - which is not fixed length, but just initials. I might have a go and post the answer here if I succeed. –  Hamish Downer Aug 24 '10 at 22:02
    
Fair enough. I posted an answer below, based on that thread. –  Telemachus Aug 24 '10 at 22:13
1  
It's also a duplicate of this one. –  Dennis Williamson Aug 25 '10 at 1:02
    
@Telemachus - it is good to refer to a solution from another site, but there is no reason a question can't exist on both sites, if it's on-topic on both. This question is on-topic on super User, it can and should be answered here. –  Gnoupi Aug 25 '10 at 7:47

7 Answers 7

up vote 2 down vote accepted

Ok, I got curious, so here's one solution:

  1. First, create a function using a slight tweak of William Pursell's answer to the SO question I link in my comment above.
  2. Next, put that in your $PS1 as \$(function_name) in the appropriate place.

As an example:

short_pwd() {
    cwd=$(pwd | perl -F/ -ane 'print join( "/", map { $i++ < @F - 1 ?  substr $_,0,1 : $_ } @F)')
    echo -n $cwd
}
# later in your .bashrc
PS1="\u \$(short_pwd) \$ "

I'm hopeful that someone more skilled in Bash-scripting than I am can suggest ways to clean up the function, but this should give you some idea of how to use the output of another command (or Bash function) in a prompt. See also here: http://www.linux.org/docs/ldp/howto/Bash-Prompt-HOWTO/x279.html

Based on your comment, I looked again and realized that my solution needs to be double quoted. If you single-quote such a function, then it will not function at all.

share|improve this answer
    
I don't know perl so can't judge the general solution, but I think you would have to edit it to use PROMPT_COMMAND as in my solution, otherwise PS1 will be set when you start bash and will not change as you change directory. –  Hamish Downer Aug 24 '10 at 23:14
    
@Hamish No, that's incorrect. This changes as you cd. I'm using it now. (And that's not a Perl thing. It has to do with how PS1 works. You can have dynamic information in there in a function via \$(function). –  Telemachus Aug 24 '10 at 23:37
    
@Hamish But you did help me to see that I needed double quotes, not single as I originally wrote. The prompt I'm testing it on here involves color escapes and is more complex, and I accidentally simplified it using single quotes. Sorry. –  Telemachus Aug 24 '10 at 23:47
    
ah interesting, I obviously need to learn a bit more about bash ... –  Hamish Downer Aug 25 '10 at 21:56

I rewrote this recently, based on another script I wrote years ago - this one is optimised to run inside bash as much as possible, to avoid costly forks. It was almost 8x faster than my old function that used awk/sed.

It does produce nice results. It keeps the pwd part of the prompt to no more than MAX_PWD_LENGTH characters, and if you're in a subdir of $HOME, it makes this clear too:

Examples:

pc770-ubu:~ $ cd ~/a/b/c
pc770-ubu:~/a/b/c $ cd d/e/f
pc770-ubu:~/a/b/c/d/e/f $ cd g
pc770-ubu:~/a/b/c/d/e/f/g $ cd h
pc770-ubu:~/a/b/c/d/e/f/g/h $ cd i
pc770-ubu:~/a/b/c/d/e/f/g/h/i $ cd j
pc770-ubu:~/a/b/c/d/e/f/g/h/i/j $ cd k
pc770-ubu:~/a/b/c/d/e/f/g/h/i/j/k $ cd l
pc770-ubu:~../c/d/e/f/g/h/i/j/k/l $ cd m
pc770-ubu:~../d/e/f/g/h/i/j/k/l/m $ cd n
pc770-ubu:~../e/f/g/h/i/j/k/l/m/n $ cd o
pc770-ubu:~../f/g/h/i/j/k/l/m/n/o $ cd /tmp/a/b/c/d/e/f
pc770-ubu:/tmp/a/b/c/d/e/f $ cd g
pc770-ubu:/tmp/a/b/c/d/e/f/g $ cd h
pc770-ubu:/tmp/a/b/c/d/e/f/g/h $ cd i
pc770-ubu:/tmp/a/b/c/d/e/f/g/h/i $ cd j
pc770-ubu:/../a/b/c/d/e/f/g/h/i/j $ cd k
pc770-ubu:/../b/c/d/e/f/g/h/i/j/k $ cd l
pc770-ubu:/../c/d/e/f/g/h/i/j/k/l $ cd m
pc770-ubu:/../d/e/f/g/h/i/j/k/l/m $ cd
pc770-ubu:~ $ 

The bash function (call this when constructing your PS1 variable):

# set this to whatever you want:
MAX_PWD_LENGTH=20

function shorten_pwd
{
    # This function ensures that the PWD string does not exceed $MAX_PWD_LENGTH characters
    PWD=$(pwd)

    # if truncated, replace truncated part with this string:
    REPLACE="/.."

    # determine part of path within HOME, or entire path if not in HOME
    RESIDUAL=${PWD#$HOME}

    # compare RESIDUAL with PWD to determine whether we are in HOME or not
    if [ X"$RESIDUAL" != X"$PWD" ]
    then
        PREFIX="~"
    fi

    # check if residual path needs truncating to keep total length below MAX_PWD_LENGTH
    # compensate for replacement string.
    TRUNC_LENGTH=$(($MAX_PWD_LENGTH - ${#PREFIX} - ${#REPLACE} - 1))
    NORMAL=${PREFIX}${RESIDUAL}
    if [ ${#NORMAL} -ge $(($MAX_PWD_LENGTH)) ]
    then
        newPWD=${PREFIX}${REPLACE}${RESIDUAL:((${#RESIDUAL} - $TRUNC_LENGTH)):$TRUNC_LENGTH}
    else
        newPWD=${PREFIX}${RESIDUAL}
    fi

    # return to caller
    echo $newPWD
}

EDIT: fixed bug with absolute string length

share|improve this answer

I like meowsqueak's approach, trying to stay in bash for performance. But I wanted my path to abbreviate long directory names down to one char.

me@comp:~ $ cd my/path/haslongnames/
me@comp:~my/p/h $

This is based on meowsqueak's solution. It could stand some improvements/more features but it solves the basic problem without firing up sed.

This is in an executable file, for instance ~/bin/ps1

# set this to whatever you want:
MAX_PWD_LENGTH=30
function shorten_pwd
{
    # This function ensures that the PWD string does not exceed $MAX_PWD_LENGTH characters
    PWD=$(pwd)

    # determine part of path within HOME, or entire path if not in HOME
    RESIDUAL=${PWD#$HOME}

    # compare RESIDUAL with PWD to determine whether we are in HOME or not
    if [ X"$RESIDUAL" != X"$PWD" ]
    then
        PREFIX="~"
    fi

    # check if residual path needs truncating to keep total length below MAX_PWD_LENGTH
    NORMAL=${PREFIX}${RESIDUAL}
    if [ ${#NORMAL} -ge $(($MAX_PWD_LENGTH)) ]
    then
        newPWD=${PREFIX}
        OIFS=$IFS
        IFS='/'
        bits=$RESIDUAL
        for x in $bits
        do
            if [ ${#x} -ge 3 ]
            then
                NEXT="/${x:0:1}"
            else
                NEXT="$x"
            fi
            newPWD="$newPWD$NEXT"
        done

        IFS=$OIFS
    else
        newPWD=${PREFIX}${RESIDUAL}
    fi

    # return to caller
    echo $newPWD
}
export PS1="\u@\h:$(shorten_pwd) $ "

In my .bash_profile I then have

PROMPT_COMMAND="source $HOME/bin/ps1"
share|improve this answer

Just add this (and edit as you like) to your .bashrc:

PS1='\u@\h:`pwd | sed -e "s/\/\(.\)[^\/]\+/\/\1/g"`\$ '
share|improve this answer
    
I tried that, but unfortunately it sets the value of PS1 once when you start bash and will not update it after that. So you will change directory and your prompt will not change - I made this mistake and then was playing and had to scratch my head to work it out. See my (edited) answer for a way around this using PROMPT_COMMAND –  Hamish Downer Aug 24 '10 at 23:12
    
No, it works for me, it changes every time. –  cYrus Aug 24 '10 at 23:16
    
And echo $PS1 gives: \u@\h:'pwd | sed -e "s/\/(.)[^\/]\+/\/\1/g"'\$ so its not hard coded into the variable (I don't know how to override this markup, just replace ' with `). –  cYrus Aug 24 '10 at 23:20
    
oh OK. I'll read some more, thanks for explaining. –  Hamish Downer Aug 25 '10 at 21:56

I've done a little more googling in the meantime, and after going through a few search terms, I came across this article that mentions the fish shell does what I want and provided a way of doing it. I modified it so the user and host are also displayed and ended up with the reasonably succinct:

# abbreviate the dir path
PROMPT_COMMAND='CurDir=`pwd|sed -e "s!$HOME!~!"|sed -re "s!([^/])[^/]+/!\1/!g"`'
PS1="\u@\h:\$CurDir \$ "

Basically every time the prompt is about to be displayed, PROMPT_COMMAND will set $CurDir to the abbreviated directory path which is then used in $PS1. Bare in mind that if PROMPT_COMMAND is set elsewhere you will need to add the above command on to the end of that one, preceded by a ;. So for the common example of setting the title of an xterm you would end up with

PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\007"; CurDir=`pwd|sed -e "s!$HOME!~!"|sed -re "s!([^/])[^/]+/!\1/!g"`'

Some other possible ways of abbreviating the path can be found:

share|improve this answer

Another version of @Telemachus short_pwd(), without perl requirement.

short_pwd() {
cwd=$(pwd)

if [ $cwd == $HOME ]; then echo -n "~"; return; fi 
if [ $cwd == "/" ]; then echo -n "/"; fi 

for l in $(echo $cwd | tr "/" "\n"); do 
    echo -n "/"
    echo -n ${l:0:1}
done
echo -n ${l:1}
}
share|improve this answer
PROMPT_DIRTRIM=3

will force \w to expand to the maximum of three trailing elements of the current working directory path, with the preceding, if any, replaced with "...".

share|improve this answer

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.