How to Use which Command in Linux: Find Executables the Right Way

Robert Johnson

You’ve just installed a new version of Python. You run python --version and… it’s still showing the old version. What gives?

This happens more often than you’d think. The problem isn’t that your installation failed. It’s that your system is finding a different Python executable first. And there’s a simple command that shows you exactly which one: which.

The which command in Linux locates the executable file that runs when you type a command. It searches through your PATH directories and returns the first match it finds. If you’ve ever wondered “why is the wrong version running?” or “where did this command even come from?”, which is your answer.

I’ve used which hundreds of times to debug everything from Python version conflicts to mysterious script failures. Let me show you how it works and when you actually need it.

What is the which Command and Why You Need It

The which command has one job: find the executable file that will run when you type a command name. It answers the question “which version of this program am I about to execute?”

RackNerd Mobile Leaderboard Banner

When you type python in your terminal, your shell doesn’t magically know which Python interpreter to run. It searches through a list of directories looking for an executable file called python. That list of directories is stored in your $PATH environment variable.

The which command mimics this search process and shows you the result.

How the which Command Works with PATH

Your $PATH variable contains a colon-separated list of directories. Here’s what mine looks like:

/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin

When you run which python, it searches these directories left to right. The first matching executable file wins. This is crucial: the order matters.

If /usr/local/bin/python exists, that’s what which returns – even if there’s also a /usr/bin/python. The first match is the one that runs when you type the command.

This behavior explains most “wrong version” problems. You installed a new Python in /usr/local/bin, but your PATH searches /usr/bin first. So the old system Python keeps running.

Real-World Scenarios Where which Saves Time

I reach for which constantly in these situations:

  • Version verification – After installing Node.js or Python, confirm the right version will run
  • Debugging scripts – A script fails because it’s calling the wrong interpreter or tool version
  • Security audits – Check if a command is coming from an expected location (or somewhere suspicious)
  • Troubleshooting installations – Verify that newly installed software is actually in your PATH
  • Multiple tool versions – When you have both Java 8 and Java 11 installed, which one runs when you type java?

Last week I spent 20 minutes debugging a CI pipeline that was failing locally but passing in production. Turned out my local machine was using /usr/local/bin/node while the container used /usr/bin/node – different major versions. One which node command revealed the problem immediately.

Basic which Command Syntax and Usage

The which command syntax is dead simple. You type which followed by the command name you want to locate.

Finding a Single Command

Here’s the basic format:

which command_name

Let’s try some real examples:

$ which python
/usr/bin/python

$ which ls
/usr/bin/ls

$ which grep
/usr/bin/grep

Each command returns the full path to the executable that will run. If the command isn’t found in any PATH directory, which returns nothing (or an error message, depending on your shell).

The output is straightforward: it’s the absolute file path to the executable. That’s all. No extra information, no formatting – just the path.

You can use this output directly in scripts or commands. For example, if you want to see detailed info about where Python lives:

$ ls -l $(which python)
lrwxrwxrwx 1 root root 7 Mar 15 2023 /usr/bin/python -> python3

This reveals that /usr/bin/python is actually a symbolic link pointing to python3. Useful information when debugging version issues.

Checking Multiple Commands at Once

You can check several commands in one shot by listing them all:

$ which git node vim
/usr/bin/git
/usr/local/bin/node
/usr/bin/vim

This is handy when you’re setting up a new development environment and want to verify multiple tools are installed and in your PATH. I do this constantly when configuring new servers or containers.

If one of the commands isn’t found, which either prints nothing for that command or shows an error (behavior varies by distribution):

$ which git node fakecommand vim
/usr/bin/git
/usr/local/bin/node
/usr/bin/vim

Notice fakecommand produced no output – it’s not in my PATH.

Using which -a to Find All Matching Executables

Here’s where which gets really useful for troubleshooting. The -a option shows all matches in your PATH, not just the first one.

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

This output tells me I have two Python executables in my PATH. When I type python, the first one runs (/usr/local/bin/python). But the second one exists too, and might be a different version.

Without -a, you only see the first match. With -a, you see what else is lurking in your PATH that could cause confusion.

Why Multiple Versions Exist

Multiple versions of the same command are super common. Here’s why:

  • System vs user installations – Your OS installs Python in /usr/bin, you install a newer version in /usr/local/bin
  • Package managers – You installed Node via apt in /usr/bin, then later used nvm which puts it in ~/.nvm/versions/node/...
  • Manual builds – You compiled something from source into /opt or ~/bin
  • Virtual environments – Python venvs, Ruby rbenv, Node nvm all add version-specific directories to PATH

This isn’t inherently a problem. But if you don’t know multiple versions exist, you’ll get very confused when your code behaves differently than expected.

Troubleshooting Version Conflicts

Let’s walk through a real debugging scenario. You’re running a Python script that suddenly breaks after you installed a new Python version.

First, check what’s actually running:

$ which -a python python3
/usr/local/bin/python
/usr/bin/python
/usr/bin/python3

Now you can see the problem. You have three Python executables. When you type python, you get /usr/local/bin/python. But maybe your script explicitly calls python3, which points to a different installation in /usr/bin.

To fix version conflicts, you have a few options:

  • Adjust PATH order – Put the directory with your preferred version earlier in $PATH
  • Use absolute paths – Call /usr/bin/python3 explicitly instead of relying on PATH search
  • Remove old versions – Uninstall or rename executables you don’t want to accidentally use
  • Use version managers – Tools like pyenv or nvm handle version switching cleanly

The which -a command doesn’t fix the problem, but it makes the problem visible. That’s half the battle.

which vs type vs whereis: When to Use Which

Linux gives you several commands that seem to do the same thing as which. They don’t. Each has different behavior and use cases.

I’ve seen countless forum posts where someone says “use which” when they really should use type, or vice versa. Let’s clear this up.

Understanding the Differences

Here’s what each command actually does:

which – Searches your PATH directories and returns the first matching executable file. It’s an external command (usually /usr/bin/which). It only searches PATH and only finds executable files.

$ which cd
(no output - cd is a shell builtin, not in PATH)

type – A shell builtin command that shows you how the shell will interpret a command. It finds aliases, functions, builtins, and executables. This is more accurate for interactive shell work.

$ type cd
cd is a shell builtin

$ type ls
ls is aliased to `ls --color=auto'

$ type python
python is /usr/bin/python

See how type gives you context? It tells you what kind of command it is, not just where the executable lives.

whereis – Searches standard Linux directories for binaries, source code, and manual pages. It looks beyond PATH, checking places like /usr/share/man for documentation.

$ whereis python
python: /usr/bin/python /usr/lib/python3.9 /etc/python /usr/share/man/man1/python.1.gz

This is useful when you want the complete picture – executable, libraries, and documentation locations.

command -v – Similar to type but outputs just the path (if it’s an executable) or the command definition. It’s defined in the POSIX command utility specification, so it’s more portable across different shells than which.

$ command -v python
/usr/bin/python

Which Command is Best for Your Use Case

Here’s when I use each one:

Use type when:

  • Working interactively in a shell and want complete information
  • You need to check if something is an alias, function, or builtin
  • You want to see how the shell will actually interpret a command
  • Debugging shell configuration or scripts

Use which when:

  • Writing portable shell scripts that need to find executables
  • You specifically need the file path for an executable (like for cron jobs for automation)
  • You want simple, parseable output without extra text
  • You’re checking multiple commands at once with which -a

Use whereis when:

  • You need to find man pages or source files
  • You want a comprehensive search beyond just PATH
  • You’re documenting where all parts of a package are installed

Use command -v when:

  • Writing POSIX-compliant shell scripts
  • You need portability across bash, dash, zsh, etc.
  • You want builtin performance (no external command execution)

My personal habit: I use type for day-to-day terminal work and which when I need the output in a script or need to check multiple versions with -a.

Common which Command Examples System Admins Use Daily

Let me show you the which commands I actually run regularly as a sysadmin. These aren’t theoretical – they’re from my shell history.

Checking Python interpreter locations:

$ which python python3
/usr/bin/python
/usr/bin/python3

This tells me immediately which Python runs when scripts call python vs python3. Critical for debugging shebang lines and virtual environment issues.

Verifying Node.js installation:

$ which -a node
/home/alex/.nvm/versions/node/v18.16.0/bin/node
/usr/bin/node

I’m using nvm (Node Version Manager), which puts a version-specific Node ahead of the system Node in my PATH. The -a flag shows both versions.

Finding Java installation paths:

$ which java
/usr/lib/jvm/java-11-openjdk-amd64/bin/java

When dealing with Java applications, knowing exactly which JDK or JRE will execute is essential. Some apps require Java 8, others need Java 11+. This shows which one runs by default.

Debugging script execution:

Let’s say you have a script that starts with #!/usr/bin/env python. That shebang line tells the system to find python in your PATH. To see exactly what will run:

$ which python
/usr/local/bin/python

Now you know /usr/local/bin/python will execute when your script runs. If that’s the wrong version, you can either change your PATH or update the shebang to use an absolute path.

Verifying newly installed software:

After installing something like Docker or AWS CLI, confirm it’s actually findable:

$ which docker
/usr/bin/docker

$ which aws
/usr/local/bin/aws

If these return nothing, either the installation failed or you need to add the install directory to your PATH.

Combining with other commands:

I frequently combine which with other tools. For example, checking when a command was last modified:

$ ls -l $(which systemctl)
-rwxr-xr-x 1 root root 1234567 Jan 15 10:30 /usr/bin/systemctl

Or using it to verify command locations before setting up system monitoring. When configuring checking systemd service status, knowing exactly where systemctl lives matters for scripting.

Understanding PATH and How which Searches It

To really master which, you need to understand PATH. It’s the mechanism behind everything which does.

Viewing Your Current PATH

Your $PATH variable is just a colon-separated string of directory paths. View it anytime:

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

For easier reading, split it into lines:

$ echo $PATH | tr ':' '\n'
/usr/local/bin
/usr/bin
/bin
/usr/local/sbin
/usr/sbin
/sbin
/home/alex/bin

When you type a command, your shell searches these directories in order, top to bottom. The first match wins.

Common directories you’ll see in PATH:

  • /usr/bin – Standard system commands installed by your package manager
  • /usr/local/bin – Locally installed or compiled software (takes precedence over /usr/bin)
  • /bin – Essential system commands needed for booting and single-user mode
  • /usr/sbin and /sbin – System administration commands (often require root)
  • ~/bin or $HOME/bin – Your personal scripts and programs

Version managers like nvm, rbenv, or pyenv modify your PATH dynamically. When you activate them, they prepend their directories to PATH so their versions of node, ruby, or python take precedence.

How PATH Order Affects Command Resolution

The order of directories in PATH is crucial. Earlier directories win over later ones.

Let’s say you have these two Python installations:

  • /usr/bin/python (Python 2.7, installed by your OS)
  • /usr/local/bin/python (Python 3.11, installed manually)

If your PATH is /usr/local/bin:/usr/bin, then which python returns /usr/local/bin/python (Python 3.11). The newer version runs.

But if your PATH is /usr/bin:/usr/local/bin, then which python returns /usr/bin/python (Python 2.7). The old system version runs instead.

This is why user-installed software often goes in /usr/local/bin – it’s typically listed before /usr/bin in default PATH configurations. Your custom installations override system defaults.

You can manipulate PATH to control which version runs. Add to the beginning of PATH:

export PATH="/opt/custom/bin:$PATH"

Or add to the end:

export PATH="$PATH:/opt/custom/bin"

The first example makes /opt/custom/bin take priority over everything. The second makes it a fallback only used if the command isn’t found elsewhere.

I’ve debugged many strange behaviors that came down to PATH order. An old version of git was running because /usr/local/bin came after /usr/bin in a user’s custom shell configuration. One PATH adjustment fixed it.

Troubleshooting with which: Real-World Scenarios

Let’s walk through some actual problems I’ve solved with which.

Scenario 1: Command not found errors

A developer complains that npm isn’t found, but they swear they installed Node.js. Check if it’s in PATH:

$ which npm
(no output)

Not found. Maybe it’s installed but not in PATH? Try whereis:

$ whereis npm
npm: /usr/local/lib/node_modules/npm

Aha. Node is installed, but its bin directory isn’t in PATH. Add /usr/local/bin (where npm symlinks live) to their PATH:

export PATH="/usr/local/bin:$PATH"

Now which npm returns /usr/local/bin/npm and the command works.

Scenario 2: Wrong Python version executing

A Python script breaks with syntax errors. The developer says it’s Python 3 code, but it’s throwing Python 2 syntax errors. Let’s investigate:

$ which -a python
/usr/bin/python
/usr/local/bin/python3

There’s the issue. python points to the old Python 2 in /usr/bin. Their script probably starts with #!/usr/bin/env python, which finds Python 2 first.

Solution: Change the shebang to #!/usr/bin/env python3 or create a symlink so python points to Python 3.

Scenario 3: Verifying installation success

You just installed Docker. Did it work?

$ which docker
/usr/bin/docker

Yes, it’s in PATH. But is it the right version?

$ /usr/bin/docker --version
Docker version 24.0.2, build 1234567

Perfect. Installation confirmed.

Scenario 4: Detecting shadowed commands

You run ls and it behaves weirdly. Someone might have created a malicious or broken ls earlier in PATH:

$ which -a ls
/home/alex/bin/ls
/usr/bin/ls

There it is. There’s an ls in ~/bin that’s being found first. Check what it actually is:

$ cat /home/alex/bin/ls
#!/bin/bash
echo "Nice try, hacker"

That’s suspicious (or maybe just a prank). Remove it and verify you’re back to the real ls:

$ rm /home/alex/bin/ls
$ which ls
/usr/bin/ls

This is also useful when reviewing SSH configuration – making sure the ssh command isn’t compromised.

Scenario 5: Security auditing suspicious executables

During a security audit, you want to verify that critical commands come from expected locations:

$ which sudo ssh scp passwd
/usr/bin/sudo
/usr/bin/ssh
/usr/bin/scp
/usr/bin/passwd

All in /usr/bin – standard location for system commands. If any showed up in /tmp or a random user directory, that would be a red flag worth investigating with system logs with journalctl.

When which Doesn’t Work: Limitations and Alternatives

The which command has some blind spots. Understanding its limitations prevents confusion.

which won’t find shell builtins. Commands like cd, echo, source, and export are built into your shell. They aren’t executable files in PATH, so which can’t find them:

$ which cd
(no output)

$ type cd
cd is a shell builtin

Use type instead when checking whether something is a builtin.

which won’t find aliases or functions. If you’ve aliased ll to ls -la or created a shell function, which doesn’t see it:

$ alias ll='ls -la'
$ which ll
(no output)

$ type ll
ll is aliased to `ls -la'

Again, type handles this correctly. According to the Bash built-in commands documentation, type is the most reliable way to understand how Bash will interpret a command.

Behavior varies across shells. Different shells have different implementations of which. Some versions are builtins (csh/tcsh), others are external commands. Output format and error handling differ.

For portable shell scripts, use command -v instead of which:

if command -v python3 > /dev/null 2>&1; then
    echo "Python 3 is available"
else
    echo "Python 3 not found"
fi

This works reliably across bash, dash, zsh, and other POSIX shells.

which only searches PATH. If a command exists outside your PATH directories, which won’t find it. Use whereis for broader searches:

$ which gcc
(no output - not in my current PATH)

$ whereis gcc
gcc: /usr/bin/gcc /usr/lib/gcc /usr/share/man/man1/gcc.1.gz

whereis found it because it searches standard system directories even if they’re not in PATH.

Bottom line: which is great for its specific job – finding executables in PATH. But it’s not a universal solution. Keep type, command -v, and whereis in your toolkit for cases where which falls short.

When troubleshooting complex systems – say, debugging why a service won’t start with checking systemd service status or analyzing what processes are actually running with the ps command for process management – understanding exactly which executables are running becomes critical. That’s where which and its alternatives shine.

The which command is simple but powerful. It answers one question clearly: “What executable runs when I type this command?” Master it, understand PATH, and you’ll debug command resolution issues much faster. When you combine it with pattern matching tools like the grep command, you can even audit your entire PATH for specific patterns or potential conflicts.

Just remember its limits and reach for type when you need the complete picture.