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.
How to install a package by name in each shell
sudo apt install -y curlDebian/Ubuntu syntax. Distro variants: `sudo dnf install -y curl` (Fedora/RHEL/Amazon Linux 2023), `sudo pacman -S --noconfirm curl` (Arch/Manjaro), `sudo zypper install -y curl` (openSUSE), `sudo apk add curl` (Alpine), `brew install curl` (macOS — no `sudo`, brew runs as the user). The `-y` flag pre-accepts the prompt — drop it for interactive confirmation.
sudo apt install -y curlsudo apt install -y curlSame OS package manager — fish has no shell-side wrinkle. CAVEAT: `sudo` under fish can confuse a few prompt frameworks because fish lacks `bash_completion`-style sudo-aware completion by default — `fish_config` to enable. The package-name expansion (`apt install ros-noetic-*`) does NOT use fish globbing — apt parses the wildcard itself (which fish would otherwise try to expand to filesystem matches and fail).
winget install --id Microsoft.PowerToys -e --source wingetpwsh 7+ on Windows 10 1809+ (winget ships built-in since Windows 11; otherwise `App Installer` from Store). `-e` = exact id-match (not a name-substring search), `--id` is the canonical identifier (`winget search powertoys` to find ids). `--scope user` installs without elevation (per-user dir, no UAC prompt) where the package supports it. For Linux/macOS pwsh, install OS package manager invocation works identically: `sudo apt install -y curl` from PowerShell on WSL2 / pwsh on macOS — pwsh shells out to the OS shell for `sudo`.
winget install --id Microsoft.PowerToys -e --source wingetSame `winget.exe` binary. cmd has no native `sudo`; elevation is per-process via `runas`: `runas /trustlevel:0x20000 "winget install --id …"`. Easier: open an elevated cmd via Start menu → "Command Prompt (Admin)". Chocolatey alternative: `choco install -y curl` (choco 0.10+; requires `choco.exe` in PATH from `https://chocolatey.org/install`). Scoop alternative: `scoop install curl` (scoop installs to `%USERPROFILE%\scoop\` — never elevates, sandboxed per-user).
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **Pick the package manager BY OS, not by shell.** On Linux, bash/zsh/fish all share the SAME OS package manager — pick by distro: `apt` (Debian/Ubuntu/Mint/Pop), `dnf` (Fedora 22+/RHEL 8+/CentOS Stream/Amazon Linux 2023), `yum` (RHEL 7 and earlier — same syntax as dnf), `pacman` (Arch/Manjaro/EndeavourOS), `zypper` (openSUSE), `apk` (Alpine — note flag style differs: `apk add`, not `install`), `emerge` (Gentoo), `xbps-install` (Void). On macOS: `brew` is the de facto standard (Homebrew, runs as user, no sudo); MacPorts (`port install`) is older / niche. On Windows: `winget` is Microsoft's built-in (modern, signed by MSFT); `choco` is the community standard (Chocolatey, older, broader catalog); `scoop` is dev-focused (per-user, no elevation). Cross-platform meta: `pkgx` / `pkgmgr` / `nix` work everywhere but require buy-in to a parallel ecosystem.
- **`-y` (yes-to-all) vs interactive prompts.** apt/dnf/zypper accept `-y` to pre-confirm; pacman uses `--noconfirm` (different flag, same meaning); apk has no flag (always non-interactive by default). winget uses `--accept-source-agreements --accept-package-agreements` for the analogous "yes to all"; without them, the first install of the session prompts in the terminal. choco uses `-y`. brew is always non-interactive (no prompt by default). CAVEAT: in CI scripts, ALWAYS pass the non-interactive flag — a Docker build that hangs on "Do you want to continue? [Y/n]" is the canonical broken-CI symptom. The full safe form for apt: `DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl`.
- **Resolving the "package name" is a research step, not a guess.** Package names differ across distros: Debian/Ubuntu calls it `python3-dev`, Fedora calls it `python3-devel`, Arch calls it `python`. SEARCH first: `apt search <keyword>` (Debian, slow on first call until cache built), `apt-cache search <keyword>` (faster, no network), `dnf search <keyword>`, `pacman -Ss <keyword>`, `brew search <keyword>`, `winget search <keyword>`. For "what package provides this binary?": Debian `apt-file search /usr/bin/foo` (requires `sudo apt install apt-file && sudo apt-file update`); Fedora `dnf provides /usr/bin/foo`; Arch `pacman -F /usr/bin/foo` (requires `sudo pacman -Fy` first). The "which package owns this file" task is the inverse — covered separately.
- **Elevation: sudo (Unix) vs UAC (Windows) — different mental models.** Unix sudo is a per-COMMAND elevation: `sudo apt install …` elevates only that command, password cached for ~15 min. Windows UAC is a per-PROCESS elevation: opening an "Administrator" cmd/pwsh terminal elevates the entire session; you cannot `sudo` a single command from a non-elevated cmd. (Workarounds: `gsudo` from `gerardog/gsudo` adds Unix-style sudo to cmd/pwsh; Windows 11 ships `sudo.exe` as an OPTIONAL feature in 11 24H2.) For per-user installs that AVOID UAC: `winget install --scope user --id …` (when supported by the package), `scoop install …` (always per-user), `choco install --force --pre-installer --installargs "/CurrentUser"` (not always supported). For automation in CI, ALWAYS run the runner as an already-elevated user — don't try to UAC mid-script.
- **Verify the install actually landed.** `command -v curl` (POSIX: prints path if installed, rc 1 if not — replaces older `which`). `apt list --installed | grep ^curl/` (`/` separator: `curl/jammy,now 7.81.0-1ubuntu1.18 amd64 [installed]`). `dnf list installed curl`. `pacman -Qi curl`. `brew list curl`. winget: `winget list --id Microsoft.PowerToys -e` (rc 0 = installed, rc -1978335212 = not). pwsh portable: `if (Get-Command curl -ErrorAction SilentlyContinue) { "installed" }`. For "is the version recent enough" gates in CI: `curl --version | head -1 | awk '{print $2}'` then compare with `dpkg --compare-versions` (Debian only) or string-compare with care. Don't parse `apt list --installed | grep` as a portable check — output format isn't stable across versions.
Related tasks
- 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".
- 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.
- 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.