A List Apart

Menu

Illustration by

The $PATH to Enlightenment

Open source software always involves a bit of tedious setup. While it may seem like it distracts from the end goal (solving problems using the tools), the setup process is often an opportunity to get more comfortable with one of the main tools of our trade: the command line.

Article Continues Below

The command line is inherently spooky to many people—it’s the arcane technology wielded by “hackers” and “computer wizards” in popular culture. In reality, though, it isn’t that cool. It’s a set of ridiculously simple tools created by Bell (now AT&T) employees to accomplish mostly simple tasks in the 1970s. It’s about as “space-age” as your microwave oven.

It’s also extremely useful—like going from building a house by hand to using power tools. And through a few concepts and metaphors, we can shine a light on the darkest corners of this command line.

One of the most important of these concepts is the Path.

Several front-end frameworks, CSS preprocessors, JavaScript libraries, and other web development tools rely on either Ruby or Node.js being installed on your machine. Bower is one such tool. Invariably, these tools will lead you to interact with the Path. That’s because the Path will need to be aware of all the tools you install for your development environment in order for your command line to function properly.

Understanding how the Path works may feel like a step backward, but the more often you use command-line tools, the greater the chances the Path will cause you problems. Before you lose hours of your day—or start throwing heavy things at your screen—let’s walk through the basics of using the Path.

A humble little variable

$PATH, as denoted by the dollar-sign prefix and the shouty uppercase, is a Unix environment variable. What is stored inside this variable is a colon-delimited list of directory paths. Something like:

/root/directory/binary:/root/other_directory/other_binary

If you’re a variable-naming aficionado, you might wonder why it’s not named $PATHS, since it contains multiple paths. If I had to guess, the singular name probably refers to “the load path composed of multiple individual paths.” Let’s go with that.

Now, if you’re curious which other kinds of environment variables exist on your system, you can type in the env command in your own command line prompt. Hit Enter and you will see a list of all the environment variables that currently exist.

Since $PATH is a variable, it can be modified as you wish, on the fly. For instance, you could run this in your shell:

$ export PATH=banana

What does this do? Well, try to run the export command above in a new window inside your terminal or in whichever shell app you use, such as Terminal on OS X.

Next, type any basic Unix command like ls (list directory contents). You’ll now see -bash: ls: command not found when ls used to work like a charm.

This sneaky sabotage is useful because now we know that without the content inside our $PATH, shit just goes…bananas.

But why? Because as many load paths do (including in programming languages and web frameworks like Rails), this Path determines what can be executed in your shell. If your shell can’t find anything to match the name you typed, it can’t run it.

Oh, by the way, just quit and restart your shell application in order to restore all your commands. This was a temporary sabotage. Just be careful to never save this inside your ~/.bash_profile. That would be really bad.

A tale of so many binaries

In Unix, some executable programs are called binaries. That’s honestly a pretty poor name since it focuses on their format instead of their function. When you write a Unix program to accomplish a task, you sometimes need to compile its source code before it can be executed. This compiling process creates the binary. Instead of using plain text (like source code), these files use some binary format to make instructions easier for a computer to process.

Unix comes with multiple directories in which to store binaries. You can see which directory is the default used to load binaries in the /etc/paths file.

# the cat command can print the content of a file
$ cat /etc/paths 
/usr/bin 
/bin 
/usr/sbin 
/sbin 
/usr/local/bin

The file contains one directory per line. The paths are listed in a meaningful order. When a binary is found in one path, it is loaded. If a binary with the same name is found in another path, it is ignored. Therefore, paths listed earlier take precedence over paths listed later.

This is why it’s common to have problems when trying to install a binary for something that already exists on your system. In the case of OS X, if you try to install a different version of Git than the one that comes with the system, you’ll run into such an issue. That’s a bummer because Git 2.0 is really nice.

If I cd (change directory) into /usr/bin—a common directory to store binaries—and run ls, I receive more than 1,000 results. That’s not really helpful. That said, if I use [grep](http://en.wikipedia.org/wiki/Grep) with ls | grep git instead, I can filter only the results of the ls command that contain git.

$ ls | grep git 
git
git-cvsserver
git-receive-pack
git-shell 
git-upload-archive 
git-upload-pack

Sure enough, there was a binary for Git inside of /usr/bin. A clean OS X installation should return /usr/bin/git when you run which git:

$ which git 
/usr/local/bin/git

Why is mine different, then? We can have an even better idea of what’s going on by using the -a option when using which:

$ which -a git
/usr/bin/git
/usr/local/bin/git

This tells me that there are two versions of Git installed on my system. Only the first one is used when I execute git commands on my command line.

Changing paths

Using a package manager for OS X called Homebrew, I installed my own version of Git because I like to have control over the tools I use every day and update them when I feel like it. I could update the system-installed Git from OS X, but I have no idea what other binaries or apps depend on it.

We saw that binary files are looked up depending on the order stored in a file called /etc/paths, so why not change that order?

Inside of the /etc/paths file, I can see that the /usr/local/bin folder in which my Homebrew-installed version of Git is located comes last. This means the git binary inside /usr/bin will take precedence over it, and my fancy new version of Git will be ignored. That’s no good.

Now, you could try to modify the order in /etc/paths so that it suits your needs by putting /usr/local/bin at the very top. The Homebrew-installed version of Git would then load first. But despite how many times you see this advice repeated in Stack Overflow discussions, don’t do it. Ever. Configurations stored in /etc/ affect the entire system. They’re not there to be changed by individual users (yes, even if you’re the only one using your machine), and you could very well cause some unforeseen issues by tinkering in there. For instance, some utility used by OS X could be relying on the original order of /etc/paths.

Instead, you should modify the $PATH in your environment, using your .bash_profile—the one stored in /Users/yourusername/.bash_profile.

All you need to do to ensure /usr/local/bin is looked into first is to include the following in your .bash_profile:

# inside /Users/olivierlacan/.bash_profile
export PATH=/usr/local/bin:$PATH

This exports a new $PATH environment variable by printing the existing one and simply prepending the /usr/local/bin path on the left of all other paths. After you save your ~/.bash_profile and restart your shell, this is what you should see when you call echo on the $PATH:

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

As you can see, /usr/local/bin is mentioned twice in the $PATH, and that’s fine. Since it’s mentioned first, all the binaries that will be loaded the first time around will be ignored when it is visited last. I honestly wish there were a safe and simple way to change the order of paths, but most solutions I’m aware of are a bit too complex. You could always override the default $PATH altogether, but that’s assuming you know exactly what you’re doing and what paths to include.

A fork in the path

Now that you’ve changed the $PATH to your liking, you can check that the proper binary is being called when you use the git command:

$ which git 
/usr/local/bin/git 

$ git --version 
git version 2.0.0
/usr/bin/git --version git version 1.8.5.2 (Apple Git-48)

There you go. Git 2.0.0 (the Homebrew-installed version) is now the one answering git commands, and the Apple-installed 1.8.5.2 version recedes in the background. If you’d rather not use git 2.0.0, you can simply uninstall it and the default version will take over seamlessly.

Protect your path

A host of utilities for developers (and designers) will automatically inject code into your .bash_profile upon installation. Often they don’t even mention it to you, so if you find odd paths listed in your profile, that may explain why loading a new session (which happens when you open a new shell window or tab) takes more time than it should: a bloated $PATH might take a while to load.

Here’s my path today:

/Users/olivierlacan/.rbenv/shims:/Users/olivierlacan/.rbenv/bin:/usr/local/bin:/usr/local/heroku/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/MacGPG2/bin

It’s a little hard to read, so I tend to break it into lines. You can do this easily with the tr command (translate characters):

$ echo $PATH | tr ':' '\n'
/Users/olivierlacan/.rbenv/shims
/Users/olivierlacan/.rbenv/bin
/usr/local/bin
/usr/local/heroku/bin
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
/opt/X11/bin
/usr/local/MacGPG2/bin

There’s a lot of stuff going on here, but it’s much easier to understand with some verticality. Try it out, and if you don’t know why one of those lines is in your $PATH, make it your goal to figure it out. You might just learn something useful.

Being more aware of your $PATH and how it functions may not seem like the most tangible piece of knowledge. Yet, as a web craftsperson you’ll likely have to interact with command line tools while you work—and someday, something may go wrong with one of these tools. Now that you know what your Path is, what it looks like when it’s clean, how to modify it properly, and how to check that it’s aware of your tools, there’s a good chance you’ll spend minutes instead of hours to get back on your own path: the one where you build things for people.

About the Author

23 Reader Comments

Load Comments