Add custom bash autocomplete function to quickly move into code projects

White bunny sitting on pebbles next to a patch of grass.
Hopping from project to project we are.

I keep my projects in a Projects directory on my Mac. It's also not unusual for me to switch back and forth between projects throughout the day. To do that, I have a couple different options:

  1. cd ~/Projects/dashboard
  2. cd ../../dashboard

Either I type a few extra characters or I have to think about where I am in relationship to where I want to go.

I thought to myself: wouldn't it be nice if I could:

  1. Type go and be in my ~/Projects directory?
  2. Better yet: Type go dashboard and be in ~/Projects/dashboard?
  3. Or better yet: Type go da, hit tab, and it autocompletes?

After a little bit of tinkering, here's the solution I came up with.

As an aside, Zsh is now the default on the Mac instead of Bash. Zsh is an extension of Bash, and while they're very similar, they do have differences. The functionality below was tested using Zsh.

Create custom cd function

Let's add a function called go to ~/.zprofile. Open ~/.zprofile for editing:

vi ~/.zprofile

And then add the following:

go() {
  cd ~/Projects/$1
}

Save your changes by pressing ESC, type :wq, and press ENTER. If you restart Terminal, you'll see this actually gets us pretty close.

The $1 refers to whatever we add after go. It's our first argument so to speak. If you typed in go hello world, then $1 = hello and $2 = world.

We can now type go and press ENTER to change into ~/Projects. We can also type go dashboard to directly move into ~/Projects/dashboard. What we can't do is enter a partial directory like go da and press TAB to autocomplete from our projects.

Create autocomplete function

Let's add another function called _go_auto_complete to ~/.zprofile:

_go_auto_complete() {
    local file
    for file in ~/Projects/"$2"*; do
        [[ -d $file ]] || continue
        COMPREPLY+=( $(basename "$file") )
    done
}
  • local file creates a local variable called file
  • for file in ~/Projects/ loops over everything within Projects
  • $2 is our partial directory name (e.g. da) that we want to autocomplete
  • * is a wildcard character to include anything that starts with da
  • I'll explain why it's $2 and not $1 in the next section
  • -d $file ensures we only autocomplete directories, not files
  • If $file is a directory, go to next line, else continue with next iteration
  • COMPREPLY is an internal bash array that stores possible completions
  • += is how we append to that array
  • $file is the full path, but we only want the directory
  • $(basename "$file") then ensures we only get the directory

Enable autocomplete function

We have a go function for us and a _go_auto_complete function for bash, but bash doesn't yet know of its existence. Let's solve that now.

Edit ~/.zshrc by running vi ~/.zshrc and include the following line:

complete -F _go_auto_complete go
  1. complete is a built-in bash command for auto completion
  2. -F indicates what we're providing is a function
  3. _go_auto_complete indicates what our autocompletion function is called
  4. go indicates to run this autocompletion function whenever we use go
  5. That's why we used $2 in the last section, because $1 would refer to go

Save your file, restart your Terminal, and add a few directories to ~/Projects (or wherever you keep them) and watch them autocomplete.

By the way, we put this command in the ~/.zshrc file because every time we open Terminal (aka start a shell session), all commands in this file get executed, ensuring our autocomplete is always ready to go– pun intended.

If you have any questions or comments, feel free to leave them below.

Featured image by Pablo Martinez.