Find which package owns a file
Given a file path on disk — typically a binary in `/usr/bin`, a library in `/usr/lib`, or a config in `/etc` — print the package that installed it. The inverse of "what files did this package install?" and the canonical way to figure out which package to upgrade, reinstall, or remove when a single file is broken.
How to find which package owns a file in each shell
dpkg -S /usr/bin/curlDebian/Ubuntu syntax (`dpkg -S` searches the installed-package file list). Distro variants: `rpm -qf /usr/bin/curl` (Fedora/RHEL/CentOS/openSUSE), `pacman -Qo /usr/bin/curl` (Arch/Manjaro), `apk info --who-owns /usr/bin/curl` (Alpine), `equery belongs /usr/bin/curl` (Gentoo, requires `app-portage/gentoolkit`). All accept either an absolute path or a path-suffix and return `package: path` style lines.
dpkg -S /usr/bin/curldpkg -S /usr/bin/curlSame OS-level tools — no fish-side wrinkle. CAVEAT: if you pipe `dpkg -S` output through fish-specific globs, remember fish uses `**` recursion natively whereas bash needs `shopt -s globstar` — but the `dpkg -S` argument is parsed by dpkg, not the shell, so the shell choice does not affect the search behavior.
winget list --query "C:\Program Files\curl\bin\curl.exe"Windows has NO single canonical "owns this file" tool because the OS does not maintain a unified file→package index the way dpkg/rpm/pacman do. Workarounds: `winget list` matches the install root in the registry; `Get-AppxPackage *Microsoft.WindowsTerminal*` for Store apps (file lives under `%LOCALAPPDATA%\Packages\...`); `Get-Package` (PackageManagement module) shows MSI installs. For chocolatey-managed files: `choco list --local-only` then grep manually — choco does not index by file path. For "which MSI installed this file?": `Get-WmiObject -Class Win32_Product -Filter "InstallLocation LIKE '%curl%'"` (slow, but the closest analogue).
winget list --query "C:\Program Files\curl\bin\curl.exe"Same `winget.exe`. For .NET tools: `dotnet tool list -g` then check `%USERPROFILE%\.dotnet\tools\`. For Scoop-installed files: `scoop which curl` returns the resolved path including the package name (`~\scoop\apps\curl\current\bin\curl.exe`). The legacy WMIC approach: `wmic product where "Name like '%curl%'" get Name, InstallLocation` — deprecated in 24H2, but still works on most installed boxes. Registry-direct grep: `reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall /s /f curl /d`.
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **`dpkg -S` vs `dpkg -L` — inverse operations on the same index.** `dpkg -S /usr/bin/curl` answers "which package installed THIS file?" (file → package); `dpkg -L curl` answers "which files did THIS package install?" (package → files). Both read the same `/var/lib/dpkg/info/*.list` files maintained by dpkg. Equivalents: `rpm -qf` ↔ `rpm -ql` (Fedora/RHEL); `pacman -Qo` ↔ `pacman -Ql` (Arch); `brew list --formula <pkg>` lists files for a formula but Homebrew has NO `find-owner-of-file` — see workaround below. The `-S` flag is mnemonic for "Search the file-list database"; `-L` is "List the package contents".
- **Homebrew has no native owner-lookup — use `brew list --verbose` + grep.** Unlike apt/dnf/pacman, Homebrew does not index files by path. The portable trick: `brew list --verbose --formula | grep /usr/local/bin/curl` (Intel macOS path) or `… | grep /opt/homebrew/bin/curl` (Apple Silicon). For "which formula installs this binary?" without shell tricks: read `/usr/local/Cellar/*/bin/<binary>` symlinks — Homebrew lays out `/<prefix>/Cellar/<formula>/<version>/bin/<binary>` and symlinks them into `/<prefix>/bin/`. So `ls -l /usr/local/bin/curl` reveals the formula via the symlink target. For casks (GUI apps): `brew list --cask` lists all installed casks; cask files land in `/Applications/` and `~/Library/`.
- **Search the package database for files NOT YET installed (apt-file, dnf provides).** "Which package would provide `/usr/bin/foo` if I were to install it?" is a different question, handled by `apt-file search /usr/bin/foo` (Debian/Ubuntu — requires `sudo apt install apt-file && sudo apt-file update` once to populate the contents cache; the cache is a few hundred MB), `dnf provides /usr/bin/foo` (Fedora — searches the live repo metadata, no separate cache step needed), `pacman -F /usr/bin/foo` (Arch — requires `sudo pacman -Fy` first to fetch the file-list databases). The inverse — `apt-file list <pkg>` — lists files in a package even before installing. Useful when you hit "command not found: foo" and want to know which package to `apt install`.
- **Windows: no unified file→package index, but the registry helps.** Every MSI install writes to `HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\<GUID>` with `InstallLocation`, `DisplayName`, `Publisher`. Walking that key for a path match is the closest registry-native answer. PowerShell idiom: `Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" | Where-Object { $_.InstallLocation -like "*curl*" } | Select DisplayName, InstallLocation`. For .exe files NOT installed by an MSI (portable apps, manual extracts, `choco install` with `--params`): there is no record. Sysinternals `sigcheck.exe -i <file>` is the fallback — reads the binary's embedded code-signing identity, which often names the publisher even when the registry has nothing.
- **A path inside a path: handling symlinks and `/usr/local/`.** `dpkg -S` and `rpm -qf` search by EXACT file path as recorded in their database. If you query a symlink (`/usr/bin/python` → `/usr/bin/python3.11`), they look up the symlink itself — but the symlink is usually NOT in any package (it was created by `update-alternatives` on Debian or `alternatives` on RHEL). To find the OWNER OF THE TARGET: `dpkg -S "$(readlink -f /usr/bin/python)"`. For `/usr/local/bin/<foo>`: NOTHING owns it under dpkg/rpm because `/usr/local` is by FHS convention reserved for the sysadmin / manual installs — package managers DO NOT install there. If a binary in `/usr/local/bin` is a mystery, it was installed by `make install`, a `curl | bash` script, or a manual `cp` — check `~/.bash_history` or look for a `Makefile` in your home directory.
Related tasks
- Install a package by name— Add a piece of software to your system from the OS-level package manager — `apt` on Debian/Ubuntu, `dnf` on Fedora/RHEL, `pacman` on Arch, `brew` on macOS, `winget`/`choco`/`scoop` on Windows. The "I need this tool, please install it" gesture.
- List installed packages— Print every package installed by the OS-level package manager — for inventory, security audits, reproducing a system on another machine, or "what got pulled in as a dep that I never asked for".
- Upgrade all packages— Apply available updates to every package currently installed — bring the system to the latest patch level for the OS package manager. The "make this box current" gesture: critical for security baselines, CI base-image refresh, and recovering a stale dev machine.
- Clean the package manager cache— Free disk space taken by downloaded `.deb` / `.rpm` / package archives the package manager keeps after install. Critical on small VMs, Docker images, and CI runners that accumulate hundreds of MB of cached archives across builds.