How can I shortern my command line prompt's current directory?
Asked Answered
V

13

63

I am using Ubuntu and I am tired of this long prompts in bash when I am working with some deep directory hierarchy. So, I would like to tweak my PS1 to shorten the working directory part the following way:

Currently I have:

pajton@dragon:~/workspace/projects/project1/folder1/test$

and would like to have:

pajton@dragon:~/workspace/.../folder1/test$

The truncating would occur if len($PWD) passes given threshold. I want to always keep the first path component and at least one last path component. Then as space permits, add more components taking from the right.

This is what I have currently. It works, but: 1) doesn't keep first path component, 2) doesn't respect cutting path at boundaries:

pwd_length=14
pwd_symbol="..."
newPWD="${PWD/#$HOME/~}"

if [ $(echo -n $newPWD | wc -c | tr -d " ") -gt $pwd_length ]
then
   newPWD="...$(echo -n $PWD | sed -e "s/.*\(.\{$pwd_length\}\)/\1/")"
else
   newPWD="$(echo -n $PWD)"
fi

And the result:

pajton@dragon:...sth/folder1/sample$ 

Thanks in advance!

Voltz answered 16/4, 2011 at 15:17 Comment(6)
Alternatively you could put working directory and the actual prompt on separate lines, using a linefeed (\n) in PS1.Snoddy
An alternative is to just have a newline at the end of your PS1, so you can see the whole path, but you can still type a long command without it wrapping. That's what I do anyway :)Extraterritorial
(Sorry for the duplicate comment @Snoddy - your one wasn't there when I clicked "Add Comment".)Extraterritorial
That would be a solution too, but personally I don't like 2-lines prompt. Too verbose:)Voltz
+1 for a very good question. I also don't like 2 line prompts and have head to deal with my commands wrapping in deep nested directories. But now I have set my environment as per my answer below and like it so much better :).Stablish
As an addendum, you can save a couple of characters by using an ellipsis character rather than three space ....Scaramouch
S
49

Consider this script using awk instead of sed for your case:

pwd_length=14
pwd_symbol="..."
newPWD="${PWD/#$HOME/~}"
if [ $(echo -n $newPWD | wc -c | tr -d " ") -gt $pwd_length ]
then
   newPWD=$(echo -n $newPWD | awk -F '/' '{
   print $1 "/" $2 "/.../" $(NF-1) "/" $(NF)}')
fi
PS1='${newPWD}$ '

For your example of directory ~/workspace/projects/project1/folder1/test it makes PS1 as: ~/workspace/.../folder1/test

UPDATE

Above solution will set your prompt but as you noted in your comment that it will NOT change PS1 dynamically when you change directory. So here is the solution that will dynamically set PS1 when you change directories around.

Put these 2 lines in your .bashrc file:

export MYPS='$(echo -n "${PWD/#$HOME/~}" | awk -F "/" '"'"'{
if (length($0) > 14) { if (NF>4) print $1 "/" $2 "/.../" $(NF-1) "/" $NF;
else if (NF>3) print $1 "/" $2 "/.../" $NF;
else print $1 "/.../" $NF; }
else print $0;}'"'"')'
PS1='$(eval "echo ${MYPS}")$ '

if (NF > 4 && length($0) > 14) condition in awk will only apply special handling when your current directory is more than 3 directories deep AND if length of $PWD is more than 14 characters otherwise and it will keep PS1 as $PWD.

eg: if current directory is ~/workspace/projects/project1$ then PS1 will be ~/workspace/projects/project1$

Effect of above in .bashrc will be as follows on your PS1:

~$ cd ~/workspace/projects/project1/folder1/test
~/workspace/.../folder1/test$ cd ..
~/workspace/.../project1/folder1$ cd ..
~/workspace/.../project1$ cd ..
~/.../projects$ cd ..
~/workspace$ cd ..
~$

Notice how prompt is changing when I change directories. Let me know if this is not what you wanted.

Stablish answered 16/4, 2011 at 15:43 Comment(11)
This works great! But it always keeps two last path components. Is it possible to keep variable number of last path components depending on the permitted $pwd_length? Thanks!Voltz
I know how to make the PS1 change:). What I meant is: if $pwd_length is big enough, more than 2 components of path may fit. IOW I wonder if its possible to make PS1 a function of pwd_length behaving differently based on pwd_lengthVoltz
@pajton: My answer does take care of pwd_length>14 condition in PS1 formatting. Please see if (NF > 4 && length($0) > 14) condition above where 14 is your $pwd_length value. However since as per your original question I used pwd[0]/pwd[1]/.../pwd[n-2]/pwd[n-1] PS1 format so I added 1 more sub-condition to enforce this $pwd_length>14 check once CWD is at least 4 level deep.Stablish
Yes, yes. I mean that when pwd_length is big, it'd be nice to have pwd[0]/pwd[1]/.../pwd[n-k]/<more>/pwd[n-2]/pwd[n-1] with some k>2 if pwd_length permitsVoltz
I think it would be a lot cleaner to place the awk script in a directory trunc function and export a newPWD variable with it. You can then export PROMPT_COMMAND=directory_trunc and export PS1="\u@\h (${newPWD}) > \$" something like that.Chemism
Any chance you could clean that up to be multiple lines? Terribly hard to read.Lutist
How could we change the final path's color?Alfaro
Got it: To change the final path's color, you could surround the result with e.g., [\e[0;34m] on the left and [\e[0m]\$ on the right for purple, so PS1='[\e[0;34m] $(eval "echo ${MYPS}")\[\e[0m\]\$ 'Alfaro
For some reason this isn't showing ~ for my home directoryAvulsion
The tilde needs to be escaped. I had the same issue when I tried the code.Veneer
Also, look into PROMPT_COMMAND instead of eval as a solution to the prompt being generated each time it is needed.Veneer
P
134

For people looking for a much simpler solution and don't need the name of the first directory in the path, Bash has built-in support for this using the PROMPT_DIRTRIM variable. From the documentation:

PROMPT_DIRTRIM

If set to a number greater than zero, the value is used as the number of trailing directory components to retain when expanding the \w and \W prompt string escapes (see Printing a Prompt). Characters removed are replaced with an ellipsis.

For example:

~$ mkdir -p a/b/c/d/e/f
~$ cd a/b/c/d/e/f
~/a/b/c/d/e/f$ export PROMPT_DIRTRIM=2
~/.../e/f$ PROMPT_DIRTRIM=3
~/.../d/e/f$ 

Downside: It depends on the directory level, not the length of the path, which you might not want.

Upside: It's very simple. Just add export PROMPT_DIRTRIM=2 to your .bashrc.

Pleasance answered 8/12, 2011 at 8:49 Comment(6)
Hmmmm ... this didn't work for me on OSX. I put it in my .bash_profile (which on OSX I think is appropriate ... it definitely executes on a new shell) and have tried "export PROMPT_DIRTRIM=2" from command prompt too.Trailer
It doesn't work on OSX with bash as shipped from Apple (I'm on Mountain Lion). The shipped version is 3.2.48(1)-release. If you install the latest bash using something like Homebrew, you'll get something in the neighborhood of 4.2.45(2)-release, which does support PROMPT_DIRTRIM.Roundsman
Yes, this environment variable was introduced in version bash 4.Quartersaw
Wow, in 2016 OS X is still shipping with bash 3.2.57, (c) 2007.Exile
@Exile Because bash 3.2 was the last release as GPLv2+. Bash 4.0+ is GPLv3+, and Apple has a strict policy against shipping GPLv3 software.Pleasance
I would like to be able to click this 10 TimesAstronomical
S
49

Consider this script using awk instead of sed for your case:

pwd_length=14
pwd_symbol="..."
newPWD="${PWD/#$HOME/~}"
if [ $(echo -n $newPWD | wc -c | tr -d " ") -gt $pwd_length ]
then
   newPWD=$(echo -n $newPWD | awk -F '/' '{
   print $1 "/" $2 "/.../" $(NF-1) "/" $(NF)}')
fi
PS1='${newPWD}$ '

For your example of directory ~/workspace/projects/project1/folder1/test it makes PS1 as: ~/workspace/.../folder1/test

UPDATE

Above solution will set your prompt but as you noted in your comment that it will NOT change PS1 dynamically when you change directory. So here is the solution that will dynamically set PS1 when you change directories around.

Put these 2 lines in your .bashrc file:

export MYPS='$(echo -n "${PWD/#$HOME/~}" | awk -F "/" '"'"'{
if (length($0) > 14) { if (NF>4) print $1 "/" $2 "/.../" $(NF-1) "/" $NF;
else if (NF>3) print $1 "/" $2 "/.../" $NF;
else print $1 "/.../" $NF; }
else print $0;}'"'"')'
PS1='$(eval "echo ${MYPS}")$ '

if (NF > 4 && length($0) > 14) condition in awk will only apply special handling when your current directory is more than 3 directories deep AND if length of $PWD is more than 14 characters otherwise and it will keep PS1 as $PWD.

eg: if current directory is ~/workspace/projects/project1$ then PS1 will be ~/workspace/projects/project1$

Effect of above in .bashrc will be as follows on your PS1:

~$ cd ~/workspace/projects/project1/folder1/test
~/workspace/.../folder1/test$ cd ..
~/workspace/.../project1/folder1$ cd ..
~/workspace/.../project1$ cd ..
~/.../projects$ cd ..
~/workspace$ cd ..
~$

Notice how prompt is changing when I change directories. Let me know if this is not what you wanted.

Stablish answered 16/4, 2011 at 15:43 Comment(11)
This works great! But it always keeps two last path components. Is it possible to keep variable number of last path components depending on the permitted $pwd_length? Thanks!Voltz
I know how to make the PS1 change:). What I meant is: if $pwd_length is big enough, more than 2 components of path may fit. IOW I wonder if its possible to make PS1 a function of pwd_length behaving differently based on pwd_lengthVoltz
@pajton: My answer does take care of pwd_length>14 condition in PS1 formatting. Please see if (NF > 4 && length($0) > 14) condition above where 14 is your $pwd_length value. However since as per your original question I used pwd[0]/pwd[1]/.../pwd[n-2]/pwd[n-1] PS1 format so I added 1 more sub-condition to enforce this $pwd_length>14 check once CWD is at least 4 level deep.Stablish
Yes, yes. I mean that when pwd_length is big, it'd be nice to have pwd[0]/pwd[1]/.../pwd[n-k]/<more>/pwd[n-2]/pwd[n-1] with some k>2 if pwd_length permitsVoltz
I think it would be a lot cleaner to place the awk script in a directory trunc function and export a newPWD variable with it. You can then export PROMPT_COMMAND=directory_trunc and export PS1="\u@\h (${newPWD}) > \$" something like that.Chemism
Any chance you could clean that up to be multiple lines? Terribly hard to read.Lutist
How could we change the final path's color?Alfaro
Got it: To change the final path's color, you could surround the result with e.g., [\e[0;34m] on the left and [\e[0m]\$ on the right for purple, so PS1='[\e[0;34m] $(eval "echo ${MYPS}")\[\e[0m\]\$ 'Alfaro
For some reason this isn't showing ~ for my home directoryAvulsion
The tilde needs to be escaped. I had the same issue when I tried the code.Veneer
Also, look into PROMPT_COMMAND instead of eval as a solution to the prompt being generated each time it is needed.Veneer
G
10

This is what I use based on the solutions from anubhava. It sets both the prompt and the windows title. The awk script is more readable so it can be tweaked/customized easily.

It will fold the path if it's more than 16 chars and 4 levels deep. Furthermore, it will also indicate in the ... how many directories were folded, so you get a sense of how deep the path is, ie: ~/usr/..4../path2/path1 indicates 4 levels were folded.

# define the awk script using heredoc notation for easy modification
MYPSDIR_AWK=$(cat << 'EOF'
BEGIN { FS = OFS = "/" }
{ 
   sub(ENVIRON["HOME"], "~");
   if (length($0) > 16 && NF > 4)
      print $1,$2,".." NF-4 "..",$(NF-1),$NF
   else
      print $0
}
EOF
)

# my replacement for \w prompt expansion
export MYPSDIR='$(echo -n "$PWD" | awk "$MYPSDIR_AWK")'

# the fancy colorized prompt: [0 user@host ~]$
# return code is in green, user@host is in bold/white
export PS1='[\[\033[1;32m\]$?\[\033[0;0m\] \[\033[0;1m\]\u@\h\[\033[0;0m\] $(eval "echo ${MYPSDIR}")]$ '

# set x/ssh window title as well
export PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007"'

Here's what it looks like in action. The green 0 is the return code of last command:

enter image description here

Greatgrandaunt answered 5/3, 2013 at 21:56 Comment(0)
O
5
echo -n $PWD | sed -re "s|(~?/[^/]*/).*(.{$pwd_length})|\1...\2|"

sed with -r only for convenience, allows to omit backslash before parentheses, and "|" as delimiter only for convenience too - because we want to use the slash inside the command. I guess your home get's displayed as ~ as well, so ~/foo/bar/baz/ should end in ~/foo/.../baz, and /foo/bar/baz/ as /foo/.../baz/.

So we take an optional ~, followed by slash, name, slash as \1, then something, then the rest as \2.

Oligosaccharide answered 16/4, 2011 at 15:45 Comment(2)
Thanks. It keeps the first path component. But, it doesn't respect "/" boundaries: pajton@dragon:~/workspace/...der1/test$Voltz
Then try this echo -n $PWD | sed -re "s|(~?/[^/]*/).*(/.{14,})|\1...\2|" please, and replace 14 with $pwd_length, if it works; I will update my posting then.Oligosaccharide
B
3

Another approach, still using sed and awk to generate the prompt. This will convert your $HOME directory into ~, show you your root directory, your lowest level (current directory), and its parent, separated by .. for each directory in between.

Inside of your .bashrc (or .bash_profile on OS X):

function generate_pwd {
  pwd | sed s.$HOME.~.g | awk -F"/" '
  BEGIN { ORS="/" }
  END {
  for (i=1; i<= NF; i++) {
      if ((i == 1 && $1 != "") || i == NF-1 || i == NF) {
        print $i
      }
      else if (i == 1 && $1 == "") {
        print "/"$2
        i++
      }
      else {
        print ".."
      }
    }
  }'
}
export PS1="\$(generate_pwd) -> "

The script uses awk's built in NF variable (number of fields) and positional variables ($1, $2 ...) to print each field (directory name) separated by the ORS variable (output record separator). It collapses the inner directories into .. in your prompt.

Example of it in use:

~/ -> cd Documents/
~/Documents/ -> cd scripts/
~/Documents/scripts/ -> cd test1/
~/../scripts/test1/ -> cd test2
~/../../test1/test2/ -> pwd
/Users/Brandon/Documents/scripts/test1/test2
~/../../test1/test2/ -> cd test3/
~/../../../test2/test3/ -> cd test4/
~/../../../../test3/test4/ -> pwd
/Users/Brandon/Documents/scripts/test1/test2/test3/test4
~/../../../../test3/test4/ -> cd /usr/
/usr/ -> cd local/
/usr/local/ -> cd etc/
/usr/local/etc/ -> cd openssl/
/usr/../etc/openssl/ -> cd private/
/usr/../../openssl/private/ ->
Banksia answered 6/11, 2015 at 18:0 Comment(0)
L
1

Apart from the bash-builtin solution using PROMPT_DIRTRIM, you may want to try $(pwd | tail -c16), which is a tad simpler than most other answers, but just gives the last 16 characters of the current directory. Of course replace 16 by any number you want.

Lapoint answered 31/3, 2015 at 8:49 Comment(0)
R
1

Raychi's answer works great for me on Mac / Catalina.

Modified the corresponding section slightly for a "two deep" beginning.

if (length($0) > 16 && NF > 5)
   print $1,$2,$3,".." NF-5 "..",$(NF-1),$NF

Result will be similar to:

/Volumes/Work/..3../Python/Tutorials

The related part of my .bashrc file as a self contained, copy & paste solution is:

MYPSDIR_AWK=$(cat << 'EOF'
BEGIN { FS = OFS = "/" }
{
   sub(ENVIRON["HOME"], "~");
   if (length($0) > 16 && NF > 5)
      print $1,$2,$3,".." NF-5 "..",$(NF-1),$NF
   else
      print $0
}
EOF
)

export MYPSDIR='$(echo -n "$PWD" | awk "$MYPSDIR_AWK")'

Note that the above does not include Raychi's colorization scheme. Add this separately if you need it.

Rankins answered 2/2, 2021 at 5:39 Comment(0)
A
0

Why not just use ${string:position:length}? You can do ${string:-$max_chars} to have the last ${max_chars} of the string.

note the negative value

Allodium answered 6/6, 2015 at 19:41 Comment(1)
is this bash or PHP?Hypotaxis
W
0

Not so different from previous solutions. However, maybe a bit more readable/editable. However, no solution to the folder name boundary, only focusing on the length of the prompt.

### SET MY PROMPT ###
if [ -n "$PS1" ]; then
    # A temporary variable to contain our prompt command
    NEW_PROMPT_COMMAND='
        pwd_short=${PWD/#$HOME/\~};
        if [ ${#pwd_short} -gt 53 ]; then
            TRIMMED_PWD=${pwd_short: 0: 25}...${pwd_short: -25}
        else
            TRIMMED_PWD=${pwd_short}
        fi
    '

    # If there's an existing prompt command, let's not 
    # clobber it
    if [ -n "$PROMPT_COMMAND" ]; then
        PROMPT_COMMAND="$PROMPT_COMMAND;$NEW_PROMPT_COMMAND"
    else
        PROMPT_COMMAND="$NEW_PROMPT_COMMAND"
    fi

    # We're done with our temporary variable
    unset NEW_PROMPT_COMMAND

    # Set PS1 with our new variable
    # \h - hostname, \u - username
    PS1='\u@\h: $TRIMMED_PWD\$ '
fi

added to the .bashrc file. All parts of the prompt is updated properly. The first part is shortened if you're in your home directory. Example:

user@computer: ~/misc/projs/solardrivers...src/com/mycompany/handles$

Wheatear answered 3/7, 2015 at 5:37 Comment(0)
V
0
generatePwd(){
  set -- "`pwd | sed -e s.${HOME}.~.g`"
  IFS="/"; declare -a array=($*)
  srt=""
  count=0
  for i in ${!array[@]}; do
      # echo ${array[i]} - $i/${#array[@]}
      if [[ $i == 0 ]]; then
        srt+=""
      elif [[ $i == $((${#array[@]}-1)) ]] || [[ $i == $((${#array[@]}-2)) ]]; then
          srt+="/${array[i]}"
      else
        count=$((${count}+1))
      fi
  done
  if [[ $count != 0 ]]; then
    srt="${array[0]}/.$count.$srt"
  else
    srt="${array[0]}$srt"
  fi
  echo "${srt}"
}

export PS1

PS1="\$(generatePwd)"

Console

$ ~/.3./machine-learning/deep-learning-computer-vision
Vullo answered 26/8, 2018 at 3:33 Comment(0)
C
0

https://github.com/chrissound/SodiumSierraStrawberry

Allows you to truncate a path like:

From: /home/sodium/Projects/Personal/Sierra/Super/Long/Path/HolyAvacado

To: »Projects/Sie…/Sup…/Lon…/Pat…/HolyAvacado/

Calmas answered 24/2, 2019 at 16:32 Comment(0)
P
0

I suggest this adaptation of a small script I've seen :

function generate_pwd { 
pwd | awk -F/ 'BEGIN{ ORS="/" } END{for (i=1; i<=NF; i++){print substr($i,1,1)}}' 
}
export PS1="\$(generate_pwd) => "

You add these few lines in your bashrc and it works... normally :

/d/m/r/S/ => pwd
/d/montreui/rpmbuild/SOURCES
Pedicab answered 8/5, 2022 at 22:10 Comment(0)
S
0

We got great answers. But I remember I really could not understand all that as a beginner. For beginners who want a shorter version of the same:

For instance: longer path

Gives you: enter image description here

If you have vscode installed code ~/.bashrc Look for this block of code

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

change the small w to capital W

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\W\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\W\$ '
fi
unset color_prompt force_color_prompt

This ensures that only the last component of the working directory is printed. Save and Exit.

In the terminal, enter source ~/.bashrc

Try it

Shifra answered 1/7, 2024 at 3:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.