which
A Windows implementation of the Unix which command, written in modern C++20.
By default it prints the first match found on your PATH (like native which); pass -a/--all to list every matching executable across the entire PATH. Extension handling follows the Windows shell rules by consulting PATHEXT (so git matches git.exe, git.cmd, etc.), and results are printed with color-coded output.
Example
$ which clion
clion.exe -> A:\B\C
clion -> D:\e\f
If nothing is found:
$ which totally-not-a-real-command
[not found] totally-not-a-real-command is not on the PATH
Install
Once published to the Windows Package Manager:
winget install allankoechke.which
Otherwise grab the prebuilt which.exe from the
Releases page, or build from source (see below).
By default only the first match per command is shown. Use -a to list every
instance across the PATH:
$ which python
python.exe -> D:\Programs\Python311
$ which -a python
python.exe -> D:\Programs\Python311
python.exe -> C:\Users\you\AppData\Local\Microsoft\WindowsApps
You can also query several commands at once:
$ which git python cmake
git.exe -> C:\Program Files\Git\cmd
python.exe -> D:\Programs\Python311
cmake.exe -> C:\Qt\Tools\CMake_64\bin
Features
- Prints the first match by default;
-a/--all lists every instance across the PATH, in order.
- Honors
PATHEXT, so extensionless queries match .exe, .cmd, .bat, .com, etc.
- Also matches extensionless files (e.g. shell scripts) with the exact name.
- De-duplicates repeated
PATH entries and duplicate matches.
- Accepts multiple command names in a single invocation.
- Color-coded output: green for matches, red for "not found", cyan separators.
- Meaningful exit codes for scripting.
Requirements
- Windows 10 or newer (for ANSI color support in the console).
- CMake 3.16 or newer.
- A C++20 compiler — tested with MinGW GCC 13 and MSVC.
- Git (CMake fetches the
argparse dependency automatically).
Building
MinGW (GCC)
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build
MSVC (Visual Studio)
cmake -S . -B build
cmake --build build --config Release
The resulting binary is written to build/bin/which.exe.
Releases
A prebuilt Windows binary is attached to each
GitHub Release. It is produced automatically by CI: pushing a
version tag builds the executable and uploads it to the release page.
git tag v1.0.0
git push origin v1.0.0
The workflow lives in .github/workflows/release.yml and triggers on any tag
matching v*.
Publishing to winget
.github/workflows/winget.yml submits an updated manifest to
microsoft/winget-pkgs
automatically whenever a release is published. It computes the installer's
SHA256 and opens the pull request for you.
One-time setup:
- Fork
microsoft/winget-pkgs on the GitHub account that will open the PRs.
- Create a classic Personal Access Token with the
public_repo scope.
- Add it as a repository secret named
WINGET_TOKEN
(Settings → Secrets and variables → Actions).
The first version usually still needs a manual submission (the manifests under
packaging/winget/ are provided for that); subsequent releases are automated.
Usage
Usage: which [--help] [--version] [--all] [--no-color] [commands]...
Locate executables on the Windows PATH and list every match found.
Positional arguments:
commands one or more command names to locate (e.g. clion git python)
Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
-a, --all list all matches on the PATH instead of just the first
--no-color disable colored output
Color is automatically disabled when output is redirected to a file or pipe, or
when the NO_COLOR environment variable is set. It can
also be turned off explicitly with --no-color.
Exit codes
| Code | Meaning |
|---|
0 | All requested commands were found. |
1 | At least one command was not found on the PATH. |
2 | Invalid usage (e.g. no command name provided). |
How it works
- Reads the
PATH environment variable and splits it on ;, de-duplicating entries.
- Reads
PATHEXT (falling back to .COM;.EXE;.BAT;.CMD) to know which extensions count as executable.
- For each command, tests the bare name plus the name with each executable extension in every
PATH directory using std::filesystem.
- Stops at the first match by default, or collects every unique match when
-a/--all is given.
- Prints each match as
filename -> directory.
Project layout
which/
├── CMakeLists.txt # Build configuration (fetches argparse)
├── src/
│ └── main.cpp # Program entry point and lookup logic
├── packaging/
│ └── winget/ # Windows Package Manager manifests
├── .github/workflows/ # CI: build + publish release binary on tag
├── LICENSE
├── .gitignore
└── README.md
License
Released under the MIT License. The bundled argparse dependency is
also MIT licensed.