I use these functions a lot: down and up. They're quite useful for navigating big source trees where stuff isn't well laid-out but you don't remember exact paths.
They work kind of like z/j, but you don't have to have been there before and it will constrain to the current tree.
down SUB1 SUB2[...] does a breadth-first search down-tree from your current location, and takes you to the first directory with all the given substrings in order in its path.
This differs from just wrapping basic find . -ipath, which does depth-first. That way, down libs lands you in ./src/libs instead of ./src/a_subcomponent/foo/bar/libs.
up SUB1 does the same, but moves you up the tree instead. I didn't do multiple substrings here because I never needed them--I mostly use them with down to anchor to a subtree (e.g., down a_sub libs if I wanted the above result).
Both are case-insensitive as written. Change ipath to path in down and grep -oi to grep -o in up to make them case sensitive instead.
They may or may not be symlink safe, and may or may not be spaces-in-paths safe. Neither is in play in my source bases.
Last note, these were originally written in fish (ask me for those versions if you want them) before I ported them to bash, so may not be examples of stunning bash code. They've been tested on bash 3.2 (Mac), 4.x, and 5.0.
function joinstr {
local IFS="$1"
shift;
echo "$*"
}
function down {
if [ -z "$1" ]; then
echo "Usage: down SUBSTR [SUBSTR...]"
return 1
fi
# foo bar baz becomes *foo*bar*baz*
local pathspec=( "$@" )
pathspec="*"$(joinstr "*" ${pathspec[@]})"*"
local depth=1
local dest=""
# iterative depth first traversal to simulate breadth-first
while true; do
# Anything at all at this depth?
local first_result=$(find . -mindepth "${depth}" -maxdepth "${depth}" -type d -print -quit)
if [ -z "${first_result}" ]; then
# nope, we're done
break
fi
# How about what we're looking for?
dest=$(find . -mindepth "${depth}" -maxdepth "${depth}" -ipath "${pathspec}" -type d -print -quit)
if [ -n "${dest}" ]; then
# found it!
break
fi
depth=$((${depth} + 1))
done
if [ -z "${dest}" ]; then
echo "down: could not find path matching ${pathspec}"
return 2
fi
echo "down: changing directory to ${dest}"
cd "${dest}"
}
function up {
if [ -z "$1" -o -n "$2" ]; then
echo "Usage: up SUBSTR"
return 1
fi
# look for substr starting at parent
local dest=$(echo ${PWD%/*} | grep -oi ".*$1[^/]*")
if [ -z "${dest}" ]; then
echo "up: could not find path matching *$1*"
return 2
fi
echo "up: changing directory to ${dest}"
cd "${dest}"
}
They work kind of like z/j, but you don't have to have been there before and it will constrain to the current tree.
down SUB1 SUB2[...] does a breadth-first search down-tree from your current location, and takes you to the first directory with all the given substrings in order in its path.
This differs from just wrapping basic find . -ipath, which does depth-first. That way, down libs lands you in ./src/libs instead of ./src/a_subcomponent/foo/bar/libs.
up SUB1 does the same, but moves you up the tree instead. I didn't do multiple substrings here because I never needed them--I mostly use them with down to anchor to a subtree (e.g., down a_sub libs if I wanted the above result).
Both are case-insensitive as written. Change ipath to path in down and grep -oi to grep -o in up to make them case sensitive instead.
They may or may not be symlink safe, and may or may not be spaces-in-paths safe. Neither is in play in my source bases.
Last note, these were originally written in fish (ask me for those versions if you want them) before I ported them to bash, so may not be examples of stunning bash code. They've been tested on bash 3.2 (Mac), 4.x, and 5.0.