I'm looking for a way to implement a responsive working directory where I at least want to display the first and last folder.
It should be based on the available with in the terminal. E.g. something like $(expr
tput cols- 35)}
.
Imagine the current working directory is ~/workspace/i/keep/my/projects/project1/src
Then I would like to display the prompt like:
~/workspace/../src
If the terminal is big enough I would like to have:
~/workspace/../project1/src
or
~/workspace/../projects/project1/src
~/workspace/../my/projects/project1/src
and so forward.
If there is enough place it should even display the full path.
To win space the home dir should always be displayed like ~
.
Is this possible using a pure bash script on OSX?
If we type cd followed by nothing, cd will change the working directory to our home directory. A related shortcut is to type cd ~user_name . In this case, cd will change the working directory to the home directory of the specified user. Typing cd - changes the working directory to the previous one.
In value of PS1 , \w or \W can be used to include working directory in the prompt. Change the value of PS1 in your $HOME/. bashrc file to change it for every terminal.
By default, bash shows just your current directory, not the entire path. To determine the exact location of your current directory within the file system, go to a shell prompt and type the command pwd.
I would like to display the prompt like:
~/workspace/../src
This will display that style of path while ending the prompt with the usual $
and space:
PS1='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'\'') \$ '
Although my intention was that this work on OSX's BSD sed
, I have only tested it on Linux with GNU sed
.
This version provides only the one output format. It does not change format as the terminal gets wider.
The key element in the definition of the new PS1
is the command substitution. That command is:
pwd | sed -E -e "s|^$HOME|~|" -e 's|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'
pwd
This prints the current working directory to stdout
"s|^$HOME|~|"
If the path begins with $HOME
, replace it with ~
. Because $HOME
is a shell variable, this command has to be in double-quotes so that the shell does variable substitution on $HOME
.
's|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'
The regex consists of three parts:
^([^/]*/[^/]*/)
matches the first two directories and saves them in \1
.
.*
this matches whatever follows the first two directories up until the last directory
(/[^/]*)
matches the last subdirectory and saves it in \2
.
If there are enough directories for the regex to match, then the whole path is replaced with \1..\2
.
Suppose that we want the format of the prompt to change with the available width of the terminal, showing more of the final directories if there is space to do so. Thus, in a narrow terminal, /home/user/dir1/dir2/dir3/dir4
might be displayed as ~/dir1/../dir4
but in a wider terminal it would appear as ~/dir1/../dir3/dir4
and in a still wider terminal, it would appear as ~/dir1/dir2/dir3/dir4
.
In that case:
PS1='$(pwd|awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '\''{sub(h,"~");n=0.3*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'\'') \$ '
Because this solution requires more calculations, awk
is used.
This code allows the user to adjust how much of the width of the terminal should be taken up by the directory list. This is done by adjusting the constant in the code n=0.3*n
. As written, this restricts the directory display, if possible, to only 30% of the terminal width.
The key element of the code is this command:
pwd | awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '{sub(h,"~");n=0.3*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'
The code considers these cases:
The directory string is short enough already. In other words, its length is fits in the allotted space. In this case, it is displayed as is.
There are only three directories. For example, ~/dir1/dir2
. Our format does not allow this to be shortened. Thus, for this case, directory string is displayed as is.
There are four or more directories and the directory string must be shortened to fit in the space. In this case, the directory string is divided into the beginning, which is stored in the variable b
, and the ending, which is stored in the variable e
. The beginning contains two directories and the dot-dot string, such as ~/dir1/../
. Starting from the last directory, directories are added to the end string e
as space allows.
awk
command-F/
This sets the field separator to /
. As a consequence, each directory will appear as a separate field.
-v "n=$(tput cols)" -v "h=^$HOME"
This creates two variables that we will need. n
has the width of the terminal in columns. h
has a regex matching the the user's home directory.
sub(h,"~")
If the path starts with the user's home directory, this replaces it with a ~
.
n=0.3*n
This sets a goal for the desired width of the directory string in the prompt. I like the directory string to be not too long, so n=0.3*n
sets the goal to 30% of the width of the terminal in columns. As discussed elsewhere, this can be replaced with another formula as per your personal preferences.
b=$1"/"$2
b
is the variable that contains the beginning of the directory string. Here, we set it to the first two directories. If the path, for example, was ~/dir1/dir2/dir3
, then this sets b
to ~/dir1
.
length($0)<=n || NF==3 {print;next;}
If the complete directory string is no longer than our goal, n
, or else if there are only three directories in the directory string, then print out the directory string as is and then quit.
NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;}
If we get, then our directory string needs to be shortened. So, we add the abbreviation string /../
to the end of b
. For the first trial, we set the end string, e
, to be the last directory. (In awk
, NF
is the number of fields. So, $NF
is the last field (directory) on the line.) We then subtract from n
the length of b
and e
. The value of n
that remains is the amount of space that we have left. Then starting with the second to last directory on the line, we try to add one directory at a time to e
without going over our goal for how long the directory string should be.
{print b e;}
This prints the final directory string that is used in the prompt.
To change the amount of space taken up by the directory display, we change the command that read n=0.3*n
(which aimed for 30% width) to n=1*n
(which aims for full width even if the $
prompt overflows to the next line):
PS1='$(pwd|awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '\''{sub(h,"~");n=1*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'\'') \$ '
Depending on your preferences, you may want other formulas. For example, try n=n-10
and it will try to leave some (but not many) available spaces for you at the end of the prompt.
PM2Ring suggested that the $
prompt always be placed on the next line, to do that, we place a \n
(newline) before the \$
prompt:
PS1='$(pwd|awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '\''{sub(h,"~");n=1*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'\'') \n\$ '
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With