command line - Bash prompt: how to have the initials of directory path
2013-12
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?
Ok, I got curious, so here's one solution:
- First, create a function using a slight tweak of William Pursell's answer to the SO question I link in my comment above.
- 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.
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
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"
Just add this (and edit as you like) to your .bashrc
:
PS1='\u@\h:`pwd | sed -e "s/\/\(.\)[^\/]\+/\/\1/g"`\$ '
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:
- Have a maximum length and truncate the start if necessary
- Controlling the Size and Appearance of $PWD from the Bash Prompt HOWTO
- Truncate the end of the path - not so useful, but it has a nice explanation of trimming variables in bash.
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}
}
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 "...".
The way my bash prompt is currently configured, it shows the whole path to the current directory. This is annoying when I'm deep inside a directory tree, as the prompt becomes so long that every command wraps into the next line. How do I make it show only the last part of the path?
This is what I have in my .bashrc
:
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\007"'
;;
*)
;;
esac
Change the \w
(lowercase) to \W
(uppercase):
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\W\[\033[00m\]\$ '
^^
this one waaaaaay over here ------------------------------------------------+
Have a look at the Bash Prompt HOWTO for lots of fun details. example:
user@host:/usr/local/bin$ echo $PS1
${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u@\h\[\033[00m\]:\[\033[01;36m\]\w\[\033[00m\]\$
user@host:/usr/local/bin$ export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u@\h\[\033[00m\]:\[\033[01;36m\]\W\[\033[00m\]\$ '
user@host:bin$
The PROMPT_COMMAND
variable, if set, is a command that gets run before displaying the prompt specified in PS1
. In your case, PROMPT_COMMAND
runs an echo
statement with certain ANSI escape sequences that manipulate the titlebar of an Xterm.
If you suspect your PROMPT_COMMAND
is overriding your PS1
prompt, you can unset
it and test things out:
$ unset PROMPT_COMMAND
Finally, be sure that you're changing the PS1
definition that actually gets used. Common locations are /etc/bash.bashrc
, /etc/profile
, ~/.bashrc
, ~/.bash_profile
, ~/.profile
. The system files are generally (but not always) run before the user files.