a tiny shell environment switcher
Shell Makefile
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



env_hooker is a shell environment switcher, similar to direnv, autoenv and others.

It helps to solve problems of the following type:

I've cd'd to a directory containing a node project. I'd like to add .node_modules/bin to my PATH.


I've cd'd to a directory containing a ruby project. I'd like to switch to the correct ruby version for the project, and set up gems to install and run from a project-specific location.

In each case we want to modify the shell environment to support a particular project type's workflow. When we enter the project directory, some environment manipulation is performed, and when we leave, it's usually desirable to tear it down again.

env_hooker does this by allowing you to define hook functions that will run when a directory is entered or exited that contains a specified project file - for example, I might want to perform custom ruby setup based on the presence of a .ruby-version file.


Installation is still pretty basic. First clone the repo:

$ git clone https://github.com/urbanautomaton/env_hooker
$ cd env_hooker


# installs to /usr/local/share/env_hooker/env_hooker.sh
$ make install

Optionally specify PREFIX (default: /usr/local) to control installation location:

# installs to /opt/share/env_hooker/env_hooker.sh
$ PREFIX=/opt make install

Then source the file in your .bashrc (or wherever) and start defining hooks:

# ~/.bashrc

. /usr/local/share/env_hooker/env_hooker.sh

function enter_some_project_type() {
  # ...

function exit_some_project_type() {
  # ...

register_env_hook .somehookfile some_project_type


  1. pick a hook file and function root name (e.g. .ruby-version and ruby_project)
  2. define entry and exit functions:
  • enter_<function_root>
  • exit_<function_root>
  1. Register the hook in your shell setup: register_env_hook <hook_file> <function_root>

This is easiest shown by example. The following uses chruby to switch to the version specified in a .ruby-version file, if one is present, and resets chruby on leaving the directory.

# ~/.bashrc

. /usr/local/share/env_hooker/env_hooker.sh

function enter_ruby_project() {
  local -r project_dir=$1
  local version
  read -r version < "${project_dir}/.ruby-version"

  [[ -n "${version}" ]] && chruby "${version}"

function exit_ruby_project() {

register_env_hook .ruby-version ruby_project

(Note: I've used chruby as an example here because the pattern I've used is directly inspired by chruby's own auto-switching, and I found the testing for that project to be a fantastic learning resource. You can test bash scripts! Who knew?)

Security notes

Before adding project-local directories to your PATH, there are some security considerations to, uh, consider.

Adding any directory within a project to your PATH is a risk if an attacker can control that location. This is always liable to be the case when cloning git repositories.

For example, let's say I add a hook that, in the presence of a .nodeproject file, adds node_modules/.bin to the PATH. An attacker could construct a malicious repo of the following form:

├── .nodeproject
└── node_modules
    └── .bin
        └── ls

where ls is executable and contains:


rm -rf /

to cause havoc on your machine if you cloned, entered and listed the contents of the repo.

For this reason I recommend being very careful when adding in-repo directories to your PATH. Using env_hooker to do so is perhaps slightly safer than, say, globally adding ./bin to your path, but it's still a fairly big risk to take. If there's a way to get your package manager to install executables outside the project directory, I recommend that you do so (e.g. bundler's BUNDLE_BIN setting), since such locations can safely be automatically added to your PATH.

Similar projects

See my rambly release article for a discussion of alternative projects that you should almost certainly prefer to this one.