I found myself needing to search up the directory tree for a specific file recently, much like git does to find the .git directory above it or rbenv does to find the .ruby-version file that will tell it which Ruby runtime to use. It wasn’t as simple to figure out as I expected.
Here’s the shell function I ended up with:
findup() {
_path=$(pwd)
while [[ "$_path" != "" ]]; do
if [[ -e "$_path/$1" ]]; then
echo "$_path/$1"
return 0
else
_path=${_path%/*}
fi
done
return 1
}The first few lines of this should be pretty obvious: I’m defining a function, then setting a variable, _path, to the current working directory. Then I enter a loop as long as _path doesn’t equal an empty string.
Next, in the if statement, I look to see if the file I’m looking for is in the current directory. If so, I print its location and exit with 0, which is the exit status for success in Unix-based operating systems.
The else part of the if statement is the only tricky part about this little function. I’m using Bash parameter substitution. When I use the ${var} form to access a variable, I get its value back. I can add to this form to manipulate the return value. Using the ${var%pattern} form, I remove from the end of ${var} the shortest part that matches my pattern. My pattern in this case is /*, which is going to match everything from the last slash forward; in other words, it will remove the last part of _path, leaving me with the parent directory.
If I get to the root directory (/) and haven’t found the file I’m looking for, I return 1 to let any program using this function that I exited unsuccessfully, in proper Unix fashion.
Drop that function in your .bashrc or .zshrc and you can use it too.