Skip to content

Instantly share code, notes, and snippets.

@amonks
Last active March 9, 2017 06:20
Show Gist options
  • Save amonks/b45605a9988b2c7dc739074fec6ce295 to your computer and use it in GitHub Desktop.
Save amonks/b45605a9988b2c7dc739074fec6ce295 to your computer and use it in GitHub Desktop.

how your shell picks which program to run

I often use a program called git in my terminal.

When I type git, my shell runs the program located at /usr/local/bin/git.

I found that out just now by running the command which git:

[I] ⋊> ~ which git
/usr/local/bin/git

if you're following along, bear in mind:

  • your prompt (the thing you type to the right of) might look different than mine. Mine looks like this: [I] ⋊> ~ .
  • you might have different programs installed than I do (our gits might be in different locations), but it's very likely that we both have a program called which somewhere.

Similarly, I can type which which to find out what program is run when I type which:

[I] ⋊> ~ which which
/usr/bin/which

Rather than using the nickname, I can run the which program by describing the location precicely:

[I] ⋊> ~ /usr/bin/which git
/usr/local/bin/git

I can run the git program the same way:

[I] ⋊> ~ /usr/local/bin/git --version
git version 2.11.0

You might have noticed that my git and which programs are in different locations. How does my shell know where to look when I type a program's name without specifying where to find it?

It looks at a variable called PATH.

We can inspect the contents of this (or any) variable using the echo program.

[I] ⋊> ~ echo $PATH
/Users/ajm/bin /Users/ajm/.yarn/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /opt/X11/bin

The dollar sign means "not the word PATH, but the value of the variable called PATH":

[I] ⋊> ~ echo PATH
PATH

It's possible that your shell prints the variable's value differently than mine. Here's what it looks like if I use my copy of Bash:

bash-3.2$ echo $PATH
/Users/ajm/bin:/Users/ajm/.yarn/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin

It's extremely unlikely that your PATH variable has the same value as mine. (Your initials aren't ajm, are they?), but our shells follow the same procedure when they need to locate a named program:

procedure for locating a named program

  1. dereference (find the value of) the $PATH variable
  2. check for a file with the given name in the first location listed in the value of $PATH
  3. If there is one, that's the program! Execute it and stop here!
  4. check for a file with the given name in the next location listed in the value of $PATH
  5. If there is one, that's the program! Execute it and stop here!
  6. repeat from step 4 until there are no locations left.

We looked at the program called git on my machine at /usr/local/bin/git earlier. I also have program called git located at /usr/bin/git. What if I want to run that?

/usr/local/bin appears before /usr/bin in the value of the PATH variable, so according to the procedure for locating a named program, if I just type git I'll always get the one at /usr/local/bin/git.

If I want to run the program located at /usr/bin/git, I can do so using that precise path:

[I] ⋊> ~ /usr/bin/git --version
git version 2.10.1 (Apple Git-78)

Now let's say I want to add a new directory, /Users/ajm/my-programs to my PATH. In my shell, Fish, I can do so with the program set

[I] ⋊> ~ set PATH $PATH /Users/ajm/my-programs
[I] ⋊> ~ echo $PATH
/Users/ajm/bin /Users/ajm/.yarn/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /opt/X11/bin /Users/ajm/my-programs/

That means, define PATH's value to be its current value, then /Users/ajm/my-programs

In bash, that's like this:

bash-3.2$ PATH=$PATH:/Users/ajm/my-programs
bash-3.2$ echo $PATH
/Users/ajm/bin:/Users/ajm/.yarn/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/ajm/my-programs

But, variable assignment in the shell is only temporary. If I begin a new Fish or Bash session, my PATH won't be set anymore.

In most shells, if you have a file at a particular path, it will execute the contents of that file at the beginning of every session. So, if we set the PATH variable in this file, it will persist.

In fish, one such path is ~/.config/fish/config.fish. I can add that set command to it with my favorite text editor, or straight from my shell:

[I] ⋊> ~ echo "set PATH \$PATH /Users/ajm/my-programs" >> ~/.config/fish/config.fish

The \ before PATH means "not the value of the variable PATH, literally a dollar sign"

In Bash, a path to a script which is executed at the beginning of every session is ~/.bashrc:

bash-3.2$ echo "PATH=\$PATH:/Users/ajm/my-programs

Of course, changing that file won't affect your current session.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment