What is the difference between $PATH and $fpath?
Categories:
Understanding $PATH vs. $fpath: Navigating Your Shell's Executables and Functions

Explore the critical differences between $PATH and $fpath in Unix-like systems, particularly macOS, and learn how they govern command execution and shell function discovery.
In Unix-like operating systems, especially macOS, the shell relies on several environment variables to locate files and commands. Among the most fundamental are $PATH
and $fpath
. While both deal with finding things, they serve distinct purposes: $PATH
is for executable programs, and $fpath
is for shell functions. Understanding their roles is crucial for efficient shell scripting, command-line usage, and troubleshooting 'command not found' errors.
What is $PATH?
The $PATH
environment variable is a colon-separated list of directories that your shell searches when you type a command. When you execute a command like ls
, grep
, or a custom script, the shell iterates through the directories listed in $PATH
from left to right, looking for an executable file with that name. The first one it finds is the one it executes. This mechanism allows you to run commands without specifying their full path, making the command line much more user-friendly.
echo $PATH
# Expected output (example):
# /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin
Viewing the current $PATH
variable.
$PATH
permanently, you typically modify your shell's configuration file (e.g., ~/.zshrc
for Zsh, ~/.bash_profile
or ~/.bashrc
for Bash). For example: export PATH="/new/directory:$PATH"
.What is $fpath?
The $fpath
environment variable is specific to Zsh (Z Shell) and serves a similar purpose to $PATH
, but exclusively for shell functions. It's an array of directories where Zsh looks for function definitions. When you call a function that hasn't been explicitly defined in your current shell session, Zsh searches these directories for a file with the same name as the function. If found, the file is sourced, and the function becomes available. This is particularly useful for autoloading functions, meaning functions are only loaded into memory when they are first called, improving shell startup performance.
echo $fpath
# Expected output (example):
# /usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/5.8/functions
Viewing the current $fpath
variable in Zsh.
$PATH
, which is a colon-separated string, $fpath
is an array of strings. When modifying it, you append or prepend elements to the array, for example: fpath=(/new/function/dir $fpath)
.Key Differences and Interactions
The fundamental distinction lies in what they locate: $PATH
finds executable binaries and scripts, while $fpath
finds shell function definitions (primarily in Zsh). While both are crucial for shell operation, they operate on different types of 'commands'. A common scenario where this distinction matters is when you write a shell script that you want to be executable (placed in a $PATH
directory) versus a collection of utility functions you want to be autoloaded by Zsh (placed in an $fpath
directory).
flowchart TD User[User types command] --> Shell(Shell process) Shell --> CheckBuiltin{Is it a built-in command?} CheckBuiltin -->|Yes| ExecuteBuiltin[Execute built-in] CheckBuiltin -->|No| CheckAlias{Is it an alias?} CheckAlias -->|Yes| ExpandAlias[Expand and re-evaluate] CheckAlias -->|No| CheckFunction{Is it a defined function?} CheckFunction -->|Yes| ExecuteFunction[Execute function] CheckFunction -->|No| SearchFpath{Search $fpath (Zsh only)?} SearchFpath -->|Yes| LoadFunction[Load function from $fpath] LoadFunction --> ExecuteFunction SearchFpath -->|No| SearchPath{Search $PATH?} SearchPath -->|Yes| ExecuteBinary[Execute binary/script from $PATH] SearchPath -->|No| CommandNotFound[Command not found error]
Shell command lookup order, highlighting $PATH and $fpath roles.
The diagram above illustrates the typical command lookup order in a shell like Zsh. Notice how $fpath
is consulted specifically for functions, while $PATH
is reserved for external executables. This layered approach allows for flexible and efficient command resolution.