7

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?

6
  • 3
    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, 2010 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. Aug 24, 2010 at 22:02
  • Fair enough. I posted an answer below, based on that thread.
    – Telemachus
    Aug 24, 2010 at 22:13
  • 1
    It's also a duplicate of this one. Aug 25, 2010 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, 2010 at 7:47

8 Answers 8

2

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.

4
  • 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. Aug 24, 2010 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, 2010 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, 2010 at 23:47
  • ah interesting, I obviously need to learn a bit more about bash ... Aug 25, 2010 at 21:56
4

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"
3
  • Can you please explain how the function does not complain on the line: RESIDUAL=${PWD#$HOME} even the syntax highlighter says it's funky with the # comment
    – stagl
    Apr 11, 2016 at 4:39
  • Honestly @stagl, it has been years since i've used this prompt. I don't remember much about why any of this works. :/
    – leff
    Apr 11, 2016 at 23:43
  • 1
    Addendum. Just found this while researching soemthing else. gnu.org/software/bash/manual/html_node/… "The word is expanded to produce a pattern just as in filename expansion (see Filename Expansion). If the pattern matches the beginning of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the ‘#’ case)"
    – leff
    Apr 11, 2016 at 23:50
4
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 "...".

2

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:

2

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

1

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

PS1='\u@\h:`pwd | sed -e "s/\/\(.\)[^\/]\+/\/\1/g"`\$ '
4
  • 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 Aug 24, 2010 at 23:12
  • No, it works for me, it changes every time.
    – cYrus
    Aug 24, 2010 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, 2010 at 23:20
  • oh OK. I'll read some more, thanks for explaining. Aug 25, 2010 at 21:56
1

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}
}
0

Try this:

PS1='$(pp="$PWD/" q=${pp/#"$HOME/"/} p=${q%?};((${#p}>19))&&echo "${p::9}…${p:(-9)}"||echo "$p") \$'

It transforms

~/.vim/bundle/ack.vim/plugin

to

.vim/bund…im/plugin

transfrom

/usr/share/doc/xorg-x11-font-utils-7.5/

to

/usr/shar…utils-7.5

And when $PWD same as $HOME, show nothing.

Bonus: you could modify number of length to fit you need.

You must log in to answer this question.

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