Short PWD in BASH Prompts

It’s not uncommon to have inordinately deep directory hierarchy on your computer, especially if you’re like me and you like to give significant names to your directory. For example, if you’re using a 80×25 terminal, the location /home/steven/download/Album Photo/2009/06 jui/20-22/21-008-St-Georges-de-la-Malbaie will cause your shell prompt to wrap around the shell window quite messily. Of course, you could show only the last directory’s name, say 21-008-St-Georges-de-la-Malbaie to continue with my previous example, but that’s a bit terse, especially if you end up on a directory whose name is unexpectedly short.

The correct solution, may be, is to arrange the prompt to show adaptively long parts of the current working directory up to a given limit, and abstract parts of the path using, say, ..., and make sure the result is legible. For example, /home/steven/download/Album Photo/2009/06 jui/20-22/21-008-St-Georges-de-la-Malbaie, using a maximum prompt length of 50 would get shortened to .../06 jui/20-22/21-008-St-Georges-de-la-Malbaie, which is already much shorter yet retained its legibility and meaning.

The rules to shorten the path used as the shell prompt are 1) do not exceed a given length and 2) cut at slashes / whenever possible. To implement this algorithm, I will suppose we are using BASH, mostly because that’s the standard shell on Linux and that’s the one I use.

We get the following code (use view plain because wordpress messes up some of the symbols):

# Simple BASH function that shortens
# a very long path for display by removing
# the left most parts and replacing them
# with a leading ...
#
# the first argument is the path
#
# the second argument is the maximum allowed
# length including the '/'s and ...
#
shorten_path()
 {
  x=${1}
  len=${#x}
  max_len=$2

  if [ $len -gt $max_len ]
  then
      # finds all the '/' in
      # the path and stores their
      # positions
      #
      pos=()
      for ((i=0;i<len;i++))
      do
          if &#91; "${x:i:1}" == "/" &#93;
          then
              pos=(${pos&#91;@&#93;} $i)
          fi
      done
      pos=(${pos&#91;@&#93;} $len)
  
      # we have the '/'s, let's find the
      # left-most that doesn't break the
      # length limit
      #
      i=0
      while &#91; $((len-pos&#91;i&#93;)) -gt $((max_len-3)) &#93;
      do
          i=$((i+1))
      done

      # let us check if it's OK to
      # print the whole thing
      #
      if &#91; ${pos&#91;i&#93;} == 0 &#93;
      then
          # the path is shorter than
          # the maximum allowed length,
          # so no need for ...
          #
          echo ${x}
      
      elif &#91; ${pos&#91;i&#93;} == $len &#93;
      then
          # constraints are broken because
          # the maximum allowed size is smaller
          # than the last part of the path, plus
          # '...'
          #
          echo ...${x:((len-max_len+3))}
      else
          # constraints are satisfied, at least
          # some parts of the path, plus ..., are
          # shorter than the maximum allowed size
          #
          echo ...${x:pos&#91;i&#93;}
      fi
  else
      echo ${x}
  fi
 }
&#91;/sourcecode&#93;<br><br>


Adding the BASH function to your <tt>.bashrc</tt> is simple. You just add the two following lines somewhere convenient (probably near the end of the file):<br><br>


. ~/lib/bash/short-prompt.sh

PS1='\[33]0;$(shorten_path "${PWD}" 50)07\]$(shorten_path "${PWD}" 50)> '

The PS1 variable controls the prompt that is normally displayed within a shell window. The escape codes instruct the terminal to change the host window’s title to the current (shortened) directory as well as showing the current (shortened) directory in the current window as the prompt, followed by a >. I elected to use 50 characters as the maximum prompt length (excluding the terminating >) because that’s what I find convenient. Do not hesitate to replace 50 by some other value, like 40, 60, or whatever (as long as it is greater than zero).

Once the prompt is modified with the shortened path, you would get something like:

Examples of shortened paths displayed in the BASH prompt

Examples of shortened paths displayed in the BASH prompt

*
* *

You can learn more (in fact, pretty much everything) on BASH prompt management here. Although many things are possible with BASH prompts, I would advise you to go for the most useful and informative—that is, for you.

6 Responses to Short PWD in BASH Prompts

  1. […] be much better if the name appeared in the terminal title bar. To do so, use the same prompt I used previously, and add uname -n in […]

  2. Hello, thank you for the script, I am enjoying it.
    I’ve managed to convert the sequence of three dot to ellipsis as you can see there:

    • Steven Pigeon says:

      That works just fine! Thx! In addition to … (the ellipsis, U+0085), we could also use ∴ (the ‘therefore’ sign,U+2234) or ┄ (a box drawing or some sort, unicode U+2505).

      To emit unicode symbols from bash, we can paste them directly into the script, or use either the external printf command ( /usr/bin/printf, not the Bash built-in) or use this trick.

  3. Dmitri says:

    Though not quite as elegant as your solution, there is an environmental variable, PROMPT_DIRTRIM in BASH 4 that will trim the path in your prompt to as many levels as you specify.

    I just put
    export PROMPT_DIRTRIM=’2′
    in my .bashrc and now \w does pretty much the same thing as that script.

    • Steven Pigeon says:

      In Bash 4.x it does work properly and elegantly. At the time of writing, though, I was still using Bash 3.something (it came with my distribution).

      The only noticeable difference is that Bash (4.x) truncates paths in the form ~/…/where/you/are rather than …/where/you/are which can be fixed (if you want) with cut or a similar tool.

  4. Alexx Roche says:

    Really helpful article. I use something similar, but my function is a little smaller: (from ~/.bashrc)

    lxr_path(){ x=${1};echo $x|sed ‘s/\(\/.\)[^\/]*\//\1\//g’;}

    PS1=’\u@\h:$(lxr_path “${PWD}”)\$ ‘

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: