Easier shell directory navigation
Tags: bash, zsh, beginners, productivity
Published: 2022-11-16 21:43:14

This is another small post to highlight a useful little piece of code that solves a rather annoying problem I deal with on a daily basis.

While the solution is neither unique nor likely the cleanest method, it is one that's simple enough that others can likely find utility in expanding.

Suppose you're in the following situation in your dev environment:
```
$ cd ${PROJ_DIR}
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz/network-interface/interface/impl
```

Suppose further that you'd like to periodically jump between several different directories. It's really annoying doing `cd ../../../` and so on, though you do have tools like `cd ${OLDPWD}` or `cd -` and such via BASH/ZSH. But this can still be tedious.

In comes the following shell method:
```
cdback() {
  local -r dirname=${1}
  [[ -z ${dirname} ]] && {
    echo "Missing required parameter: Parent Directory Name\n"
    return 1
  }
  ## Note: https://stackoverflow.com/questions/22537804/retrieve-a-word-after-a-regular-expression-in-shell-script
  ##       ZSH uses ${match} for array while bash has ${BASH_REMATCH}.
  ## Note: https://stackoverflow.com/questions/1335815/how-to-slice-an-array-in-bash
  ##       Made use of nested ${..} bash constructs to rebuild directory path.
  ## Note: Had to do the bash version a bit differently due to "substitution error" with nested ${..} construct.
  ##       Also, array indexing was different between zsh and bash for some reason...
  local -r pattern="^(.+)\/(${dirname})\/(.*)$"
  [[ ${PWD} =~ ${pattern} ]] && {
    [[ -n $ZSH_VERSION ]] && cd "${${match[@]:0:2}/ //}" || cd "$(echo ${BASH_REMATCH[@]:1:2} | tr ' ' '/')"
  } || {
    echo "Parent Directory '${dirname}' could not be found\n"
  }
}
```

The idea behind this method is that we can use the power of regex to jump up the directory tree to a directory with a name that matches the first argument to the `cdback` method.

For example:
```
$ cd ${PROJ_DIR}
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz/network-interface/interface/impl
$ cdback mybiz
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz
cd ${OLDPWD}
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz/network-interface/interface/impl
```

This even works by repetition:
```
$ cd ${PROJ_DIR}
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz/network-interface/interface/impl
$ cdback network
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz/network-interface
$ cdback network
$ pwd
~/dev/shared/network-interface/dev/shared/network
$ cdback network
$ pwd
~/dev/shared/network-interface
```

If you want to avoid repetition for similarly named directories, you can include parent/child directories like so:
```
$ cd ${PROJ_DIR}
$ pwd
~/dev/shared/network-interface/dev/shared/network/src/main/java/com/mybiz/network-interface/interface/impl
$ cdback shared/network-interface
$ pwd
~/dev/shared/network-interface
```

There's more elegant ways to set this up and to support directories with spaces (but who does that really), but I found this setup immediately useful. Especially for heavily nested project directory structures.

Any comments/criticism are welcome! Hopefully folks find this useful.