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#section1

$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#section2

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#section3

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#section4

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#section5

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

Olivier Lacan

Olivier Lacan is a software developer at Code School. Building half a ship as a web designer didn’t satisfy him, so he became addicted to Ruby programming. He now writes on various topics and tries to get the open source software world to be more welcoming.

26 Reader Comments

  1. Great post, Olivier. I hope ALA continues to publish more content like this. As a designer/front-end developer I have a decent grasp of the command line, but I’m always interested in things like this.

    Would love to see more topics about the CLI, especially using Unix tools like grep, awk, sed, tmux, etc.

  2. Ryan, good to know. And I’m glad you enjoyed it. I think grep is a good next target although it’s a bit less foundational but I’ll see what I can do. 🙂

  3. After all this *nixing you’d think I’d be aware of which’s `-a` but nope. I can see it being useful. Thanks Oliver.

    Another thing that would trip me occasionally, back when I was finding my way through this environment, was how there can be multiple shell initialization files like .profile, .bashrc, .bash_profile, .zshrc etc. and they have a specific order of being read/sourced. The files in which you set your $PATH thereby affects the final order within $PATH. Just a note 🙂

  4. Sri, that’s a good point and I only stuck to `.bash_profile` because it’s the default on OS X. It’s indeed a hairy topic because you end up having to explain the difference between interactive vs. non-interactive shells, and login vs. non-login shells.

    I think the most digestible and concise explanation I’ve found regarding those is on the Unix StackExchange site.

    A slightly more authoritative document (but less concise) would be the Bash manual on gnu.org.

  5. Thanks for this! The first A List Apart article I’ve read completely (without simply scanning) in a few years. I would really like to see more of this kind of writing on ALA.

  6. It’s a slippery slope, man. Today it’s $PATH and git, and then next you’re shell scripting and experimenting with virtualenv and rvm. Before you know it, you’ve got Linux in a Virtualbox. You’re learning vim. Eventually, your office looks like the set of the IT Crowd. EFF stickers on your lunchbox. That way lies madness! I know! It’s too late for me. Save yourselves if you can.

  7. Thanks for this! Just the translate function alone is a life-saver.

    I’m a little unclear on the difference between `cat /etc/paths` and `echo $PATH`. What’s the difference between the two exactly?

    Also, I assume path directories are not recursive, meaning contained directories must be explicitly specified.

  8. Really great introduction! I was intending to write something similar, but I think everything I would want to cover is here. I get many users on our research servers opening accounts and not having a clue about basic Unix concepts such as the $PATH. Now I have a link to get them started 🙂

  9. Thank you for this succinct description of $PATH. I’ve been using Linux for a while and have a basic working proficiency with the command line. This certainly fills in some of those foundational holes in my knowledge.

    @skube – the difference between the above cat and echo commands: cat prints the contents of the ‘paths’ file found in the ‘etc’ folder, while echo is simply printing out the contents of the ‘$PATH’ variable.

    Basically /etc/paths are the defaults, while $PATH is the actual loaded-into-memory paths which are being used , for that session.

    When we log into a server, or open a new terminal window, the $PATH is constructed from the defaults, as well as from instructions specific to that user, and the way in which they are logged in. Those ‘user specific instructions’ vary depending on the system, but examples would be .profile, .bashrc, .bash_profile located in the home folder of that user.

    Since $PATH is what is being actively used, setting $PATH to ‘banana’ screws things up – there is no command “ls” in the directory “banana” (unless you created it of course!)

  10. Linux Pocket Guide 2nd Edition (as of this writing) by Daniel Barrett is a must have for those wish to learn more about bash or just need a simple reference guide.

  11. Nice article, though I’m wondering… by giving preference to the path that contains the version of Git you prefer, aren’t you at the same time, blindly giving preference to all the binaries in that directory? Wouldn’t that be potential for a nasty gotcha?

  12. Mark, not really. As I showed it’s easy to see which binary is being used with `which ` and if you use Homebrew, I expect you to at least be aware of what you installed, otherwise you can always run `brew list` to check.

  13. Thanks for the walk through! While I’ve edited my $PATH multiple times, either through direct instruction or various installations, this was a helpful explanation of the basics that I wish I had a better understanding of from the start. I even found that my /etc/paths file had been edited (who knows when) with /usr/local/bin sitting on top! I decided to heed your warning and reverse it. Cheers.

  14. Thank you so much for this article! The PATH has been the bane of my web dev life for a while, and I’ve always been in too much of a rush to research how it works properly. But after having to re-install my machine again the last few days, I thought I’d spend more time researching how everything works properly, with the PATH being one of my main struggles. So this article has helped massively 🙂

  15. This is an amazing article. I am new to this community. So thanks for accepting me and sharing this.
    I also used $path several times and its great bane for developers.

    By the way, I recently wrote an article on Laravel tutorial. Hope you guys like it.

    Thanks,
    Parth Patel

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

Nothing Fails Like Success

Our own @zeldman paints the complicated catch-22 that our free, democratized web has with our money-making capitalist roots. As creators, how do we untangle this web? #LetsFixThis