I had an idea a while ago that one of the biggest annoyances of the
standard prompt is the length of the full name of the working directory.
Some people use the Bash \W
prompt string escape, which is just the
last directory name instead of \w
which is the full path, but I
wanted something in between the two because the final directory name can be
something common. If you work with Ansible, you're familiar with this:
every project has a folder called "tasks/" and how do you know which
project you're in from that? I thought it would be good if we could have
the first letter of every directory except the last one ... although the
idea requires some specialty caveats, such as if the first character of a
directory name is a "." I want to preserve one more letter, and if
$HOME
is part of the working directory, I want it replaced with "~".
Let's take the construction in steps.
First Version
PS1="\$ "
PROMPT_COMMAND="echo \"\${PWD}\" | sed -e 's@'${HOME}'@~@' -E -e 's@(\.?[^/])[^/]*(/)@\1\2@g'"
I freely admit I don't have a great grasp on that second sed
expression: I got assistance from William and Angelo. The first expression
replaces $HOME
with "~", the second replaces directory names with the
first letter of the name - unless there's a dot, in which case that's saved
too.
You can paste these two lines in a terminal and you should see something like this:
~$ cd /usr/bin
/u/bin$ cd ~/.config/autostart
~/.c/autostart$
Second Version
From here on I'd recommend copying the commands into a text file, and then sourcing the file: it becomes trickier to paste some of the more complex commands below directly at the prompt.
The next revision is just to chop off the last directory name:
prompt_command ()
{
newPWD=$(echo "${PWD/${HOME}/\~}" | sed -E -e 's@(\.?[^/])[^/]*(/)@\1\2@g' -e 's/[^/~]*$//' );
export newPWD
}
PROMPT_COMMAND=prompt_command
PS1="\${newPWD}\W\$ "
This raises the question: why remove the last directory name when you're
only going to replace it with \W
which is practically the same? The
answer is so we can colourize that last name separately in the next step.
This version of the prompt also has a flaw: when you're in your $HOME, it
shows "~~" instead of just "~" as it should. We'll fix that.
Third Version
In one big change, we'll add colourization and a couple new features. If the directory you're in isn't writeable, the final directory name will be colourized. And we'll show the return value (colourized, differently) if it's anything other than zero. And just to get fancy and a bit garish, we'll colourize all the slashes in the directory path red.
BG_YELLOW="\[\e[43m\]"
BLACK="\[\e[1;30m\]"
BG_GRAY="\[\e[47m\]"
RED="\[\e[1;31m\]"
NOCOLOUR="\[\e[0;0m\]"
prompt_command ()
{
# record the return value from the last command before we run other
# commands (which would modify it):
_p_retval="$?"
if (( _p_retval == 0 ))
then
# if the return value is zero, discard it:
_p_retval=""
fi
newPWD=$(echo "${PWD/${HOME}/\~}" | sed -E -e 's@(\.?[^/])[^/]*(/)@\1\2@g' -e 's/[^/~]*$//' );
export newPWD
}
PROMPT_COMMAND=prompt_command
# use Bash parameter expansion to replace each slash with a red slash:
PS1="$( echo -e "\${newPWD//\//$(echo -e ${RED})\/$(echo -e ${NOCOLOUR})}" )"
# check if $PWD is writeable, if not, insert a colour code:
PS1="${PS1}\$( if ! [ -w \"\${PWD}\" ]; then echo -en \"${BG_YELLOW}${BLACK}\"; fi )"
# if we're in $HOME, don't double-up on the tildes:
PS1="${PS1}\$( if [ \"\${PWD}\" != \"${HOME}\" ]; then echo \"\W\"; fi )"
# colourize the retval, which will only show if non-zero:
PS1="${PS1}${BG_GRAY}${RED}\${_p_retval}${NOCOLOUR}\$ "