# zsh function for shrinking $PWD with$HOME replacement

I wanted a $PWD shrinking for my $PROMPT with the following requirements:

• $HOME is replaced by ~ • All subpaths except the last one are replaced by their initial letters • Reasonably efficient This is my attempt, with tests: #!/bin/zsh function shrinkpath() { local dir="${1/#$HOME/~}" if [$dir = "~" ]; then
echo "~"
return
fi
local split=(${(s:/:)dir%/*}) local prefix="/" if [ "$split[1]" = "~" ]; then
prefix=""
fi
local mid=$(for i ("$split[@]") ; do echo -n "${i:0:1}/" ; done) local last="${dir##*/}"
echo "$prefix$mid$last" } echo "$(shrinkpath $HOME)" echo "$(shrinkpath $HOME/foo)" echo "$(shrinkpath $HOME/foo/bar/baz)" echo "$(shrinkpath /)"
echo "$(shrinkpath /foo)" echo "$(shrinkpath /foo/bar/baz)"


Output:

~
~/foo
~/f/b/baz
/
/foo
/f/b/baz


Seems to be doing the trick but I feel it could be smaller/simpler. In particular, my intuition tells me that I should be able to get away with a single check for "~" instead of two, and that I might be using too many intermediate variables. I am also not sure about whether there is a better way to do the "split by / and get the first letter of each subpath" part.

## 1 Answer

As this is for a prompt:

shrinkpath () {
local split dir=${PWD/#~\//\~/} case$dir in
~) dir='~' ;;
*) split=( "${(@s:/:)dir}" ) dir=${(j:/:M)split#?}${split[-1]:1} ;; esac psvar[1]=$dir
}

precmd_functions=( shrinkpath )
PS1='%1v%# '


This is similar to your current approach (split->process->join). The # modifier combined with the (M) flag is used for removing everything except the first character in every path component (after HOME prefix replacement). The last component is appended with its first character removed.

A prefix variable is not needed as the s flag is used inside double quotes with the @ flag (man zshexpn says of s:string: "For historical reasons, the usual behaviour that empty array elements are retained inside double quotes is disabled for arrays generated by splitting" ... "To override this behaviour, supply the (@) flag as well").

psvar is an array to use with the %v sequence in the prompt string. For testing, I added print -r -- $dir to the end of shrinkpath() so that different PWDs can be passed to see what it does with them (note the fourth test where HOME appears as a prefix, but isn't the user's home): % PWD=$HOME shrinkpath
~
% PWD=$HOME/foo shrinkpath ~/foo % PWD=$HOME/foo/bar/baz shrinkpath
~/f/b/baz
% PWD=${HOME}foo/bar/baz shrinkpath /h/x/b/baz % PWD=/ shrinkpath / % PWD=/foo shrinkpath /foo % PWD=/foo/bar/baz shrinkpath /f/b/baz  Using %v in the prompt string and making use of a function to alter the psvar array should be more efficient than using the PROMPT_SUBST option with $(shrinkpath) as that approach would fork a subshell.

The * case could be changed to:

    */*/*)
dir="${(@j:/:M)${(@s:/:)dir}#?}${dir##*/(?|)}" ;;  */*/* to skip the split/join process when it's not needed. Zsh can nest parameter expansions like ${${${var}}}, so the intermediate split array is not really needed.