Writing an 'upfind' shell script

Posted on:October 25, 2023

Writing a shell script (in bash and fish) to recursively call the find unix command in parent directories.

Table of contents

Intro

In my workflow I use a command I have named upfind to recursively search upwards in directories till the query is matched. I commonly use this to jump to the root directory of my current project, or things similiar to that. Below I will describe how to make this command, in bash and then source it to your machine. Then I will provide an alternative method to writing it in fish, which will have completions, and be automatically sourced to your machine. If you think having a command like this would be useful in your dev toolkit, feel free to follow along below.

Bash

As you fill find throughtout many of my other blog posts, I highly recommend a out the fish shell if you are not already. However, below I document how to create this command in bash.

Creating the upfind command

To create an upfind command for your dev enviornment, we begin by running the following commands.

touch upfind && chmod +x upfind

Next we open the file in your editor of prefrence, and copy and paste the following snippet.

#!/bin/bash

while [[ $PWD != / ]]; do
    if test $(find "$PWD"/ -maxdepth 1 "$@" | wc -c) -eq 0
    then
        cd ..
    else
        echo $PWD;
        break;
    fi
done

Using the upfind command

./upfind -name $HOME

Moving the command into your path

You will want to then move this file into your $PATH, to allow it to be executed by your user. You can see which directories are currently sourced to your user, by echoing the $PATH variable in your shell language of choice.

It is typically recommened to move your user executables into the following directory: ~/.local/bin/

However, you can also place files into the directory /usr/bin/ or /usr/bin/local/. This could create issues though, as the executables found in these paths are system commands.

Using the bash ‘upfind’ command

After placing the ./upfind command in your $PATH you can call the command as you would any other shell command.

Fish

To write the command in fish-shell the shell code is not very different. The main pro to writing this command in fish, that we can provide completions to the command with relative ease as well.

Creating the upfind command

To create an upfind command for your fish shell dev enviornment, we begin by creating a file in one of the directories auto-sourced by $fish_function_path. I will assume you are using the default path, so create a file named upfind by using the following command:

touch ~/.config/fish/functions/upfind.fish

Writing upfind in fish shell

After you have created the upfind file, open your editor and enter the code below. This code is working in fish, version 3.6.1:

function upfind --wraps find --description "find recursively upwards"
    while [ $PWD != / ]
        if test $(find "$PWD"/ -maxdepth 1 $argv | wc -c) -eq 0
            cd ..
        else
            echo $PWD
            break;
        end
    end
end

Lets break down some of the nuances in syntax used above.

  • Passing the flag --wraps to the function command, wraps all completions to the command find to our new command upfind.
  • If we place this file in our ~/.config/fish/functions it is automatically sourced to our entire shell.
  • Using the [ ] syntax we are effectively calling the test command in fish shell. I first found out that these were syntacic equals from browsing through the tree-sitter-fish repo.

Using your new upfind command:

After you have picked a shell to implement this script, you should be able to use the upfind command however you may need it. Personally, I recommend the fish implementation because of its ease of use, as well as it’s ability to give me completions.

Examples

I use the upfind command for various cases daily but some of which can be found below:

  • finding the parent .git/ directory: upfind -name .git
  • finding the parent directory to a package.json file: upfind -name package.json
Join the Newsletter!