What is my PATH?
Published by Mark Holmes on November 7th, 2022This is the first in a series of articles I’m calling Things no one taught you that you’re just expected to know as an engineer. They’re mostly lessons I’ve learned by trial and error over the years, and I’m hoping these will be useful to someone.
I may have missed this lesson in my Operating Systems class (which was admittedly a long time ago). Or, perhaps more likely, I didn’t know I’d need it and forgot. Either way, open your terminal and run echo $PATH
; it may look something like this:
➜ ~ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
As an engineer, you’ll need to interact with your PATH
on occasion. For example, you’ll see this message as the final step of the Homebrew installation:
- Run these three commands in your terminal to add Homebrew to your PATH:
echo '# Set PATH, MANPATH, etc., for Homebrew.' >> .zshrc
echo 'eval "$(${HOMEBREW_PREFIX}/bin/brew shellenv)"' >> .zshrc
eval "$(${HOMEBREW_PREFIX}/bin/brew shellenv)"
You don’t need to think about it often, but in case trouble arises it’s good to know what your PATH
actually does.
How do I create or modify my PATH?
First, let’s assume we’re creating some bash script that echos “Hello, world!“.
#!/bin/sh
echo "Hello, World!"
You can save it anywhere, ~/Desktop/script.sh
will do fine. In order to run that script, open your terminal, change to your Desktop directory (cd ~/Desktop
), and type ./script.sh
. Another way is to open your terminal to your home directory (~
) and type ./Desktop/script.sh
. You’re executing this script at its qualified path. If you just typed script.sh
, you’d get an error: command not found: script.sh
. But what if we did want to run our script from anywhere just by typing script.sh
?
Let’s make this a little more interesting. Let’s move our script to a different folder and rename it. Run mkdir ~/tmp && mv ~/Desktop/script.sh ~/tmp/hello && chmod +x ~/tmp/hello
. This makes a new directory at ~/tmp
, moves the script to the ~/tmp
folder, renames the script to hello
, and modifies the script to allow it to be executed. Change back to your home directory (cd ~
) and try running your script!
➜ ~ hello
zsh: command not found: hello
Well, we can’t do that just yet. That’s because our PATH
variable declares the directories on our file system that have scripts that can be executed without needing each script’s full path. And there’s really nothing special about the PATH
variable, it can be modified or overwritten. If we wanted to create our own environment variable, we could do that as follows:
➜ ~ export FOO="bar"
➜ ~ echo $FOO
bar
So how do we get our hello
program to run? Let’s modify our PATH
(temporarily — if we wanted these changes to stick, we’d need to add them to our shell’s startup script):
➜ ~ export PATH="/Users/mark/tmp:$PATH"
➜ ~ echo $PATH
/Users/mark/tmp:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
(Note: Sub in your machine’s username, which probably isn’t mark
)
And then give it a whirl:
➜ ~ hello
Hello, world!
Success! We created a script, added its directory to our PATH
, and executed it from our home directory. We also used a neat little trick by using our current $PATH
variable to prepend our new script’s directory!
(And if you want to undo everything we just did, you can run rm -f ~/tmp
to remove the script and simply close and reopen your terminal to get rid of the temporary PATH
variable.)
Alright, admittedly you likely won’t be looking at your PATH
frequently. Maybe you’re setting up a new machine or installing new software. It’s not often, but it’s probably not never either. I was setting up asdf recently to manage my language versions and messed up the installation. My PATH
was set incorrectly, causing permissions issues. I ended up setting my PATH
in my ~/.zshrc
file, which now looks something like this:
# Custom path
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin:/usr/sbin:/sbin"
You’ll notice that when I run echo $PATH
, I get this:
➜ ~ echo $PATH
/Users/mark/.asdf/shims:/Users/mark/.asdf/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin:/usr/sbin:/sbin
That’s because I source asdf.sh
later in my ~/.zshrc
file, which makes its own addition to the PATH
variable:
# Source asdf
. $HOME/.asdf/asdf.sh
I bring this up because it’s not uncommon; you won’t always be setting your PATH
variable by hand. If you do encounter weird behavior, you may need to check what scripts in your ~/.zshrc
file (or you shell’s equivalent) are causing PATH
mutations.
One final thought here. Have you ever thought about what running npm
(or equivalent programs) actually does? You may have noticed the first path in my PATH
, /Users/mark/.asdf/shims
. What happens if we list the files in that directory?
➜ ~ ls -al ~/.asdf/shims
drwxr-xr-x 32 mark staff 1024 Nov 7 18:51 .
drwxr-xr-x 31 mark staff 992 Nov 4 20:35 ..
-rwxr-xr-x 1 mark staff 136 Nov 7 18:51 elixir
-rwxr-xr-x 1 mark staff 126 Nov 7 18:48 erl
-rwxr-xr-x 1 mark staff 133 Nov 7 18:51 iex
-rwxr-xr-x 1 mark staff 133 Nov 7 18:51 mix
-rwxr-xr-x 1 mark staff 128 Nov 4 20:35 node
-rwxr-xr-x 1 mark staff 127 Nov 4 20:35 npm
-rwxr-xr-x 1 mark staff 127 Nov 4 20:35 npx
I omitted a few results here for brevity’s sake, but this should look pretty familiar to our hello
script from earlier!
So with /Users/mark/.asdf/shims
in our PATH
, we can run any of the scripts provided by the languages we installed using asdf.
To say this another way, if we didn’t have that in our path, we couldn’t just run npm start
, we’d have to run /Users/mark/.asdf/shims/npm start
, and that would be really annoying. Thankfully, PATH
has us covered here.
Happy Hug a Bear Day!