A Custom Shell Function (userbin) for Simplifying Development of CL Tools
While I was packaging notes, a note-taking command line program I wrote, for distribution via Homebrew, I ran into difficulties managing my PATH
.
To make sure notes installed via brew install notes
works the same as notes in my development repo, I installed notes via Homebrew. The executable I use daily is the one living in the keg in /usr/local/Cellar
, which gets added to PATH
via a symlink in /usr/local/bin
.
However, if I want to make changes to the program, I have to add the development version of notes to my PATH
, ahead of /usr/local/bin/notes
, in order to test it. When I’m done I have to remove the development version from my PATH
so that notes references /usr/local/bin/notes
again. This can get really annoying.
To solve this problem, I wrote userbin, a shell function that creates or removes a symlink from $HOME/.local/bin
to any executable. The function accepts just one argument: the path to the executable when creating the symlink, or its name when removing it. The syntax is forgiving and intuitive:
- both
userbin any/path/<exe>
anduserbin <exe>
will remove$HOME/.local/bin/<exe>
, if the symlink exists - if it doesn’t,
userbin path/to/<exe>
will create$HOME/.local/bin/<exe>
, as long aspath/to/<exe>
is valid - multiple executables can be passed, and userbin will attempt to link or unlink all of them
This function depends on $HOME/.local/bin
coming first in your PATH
. This is as easy as adding PATH="${HOME}/.local/bin:${PATH}"
to the end of your startup script.
userbin is one of various functions I define in ~/.helpers.sh
, a script I source in .zshrc
. It’s useful for anyone developing and testing a command line tool themselves. Here’s the source code:
function userbin(){
bin="${HOME}/.local/bin"
while test $# -gt 0; do
exe=`basename $1`
tgt="${bin}/${exe}"
src="$(pwd)/$1"
if [ -L $tgt ]; then
rm $tgt
echo "removed ${exe} from ${bin}"
shift && continue
fi
if [ -f $src ]; then
ln -s $src $tgt
echo "linked ${src} to ${tgt}"
shift && continue
fi
echo "the file ${src} doesn't exist"
return 1
done
return 0
}