Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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}"
  }


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: