Computing FoundationsThe Command Line › Day 13

Hands-on lab — Day 13: Package Managers: Homebrew, apt, and winget

Commands

Setup

cd labs/sections/computing-foundations/day-013-package-managers-homebrew-apt-and-winget

Run

bash examples/detect_pkg_manager.sh
bash starter/detect_pkg_manager.sh

Test

bash tests/run_tests.sh

File tree

examples/command-cheatsheet.md
examples/detect_pkg_manager.sh
expected-output/FIELDS.md
expected-output/sample-macos.txt
metadata.yml
README.md
requirements/README.md
security.md
starter/detect_pkg_manager.sh
starter/package-manager-worksheet.md
tests/run_tests.sh
troubleshooting.md

Lab README

Day 013 lab — Know Your Package Manager

Lesson

Purpose

Day 13's lesson explains what package managers are and why they matter. This lab makes it concrete and safe: you inspect your own machine to discover which package managers it already has, run read-only queries against them, and record how you would install software — without installing anything. It is a look-before-you-leap tour of the software that manages all your other software.

Learning objectives

  • Detect which package managers are present on your machine with command -v.
  • Run safe, read-only queries against a manager (version, list, repository info).
  • Identify your operating system's default system package manager.
  • Read a cross-manager command cheat sheet and map a task across brew/apt/winget.
  • Explain why a manager install is safer and more repeatable than a manual one.

Prerequisites

  • The Day 13 lesson (read it first — it explains every concept this lab inspects).
  • A terminal: Terminal.app (macOS), any terminal (Linux), or PowerShell/WSL (Windows).
  • Comfort running basic commands (Days 8–12). No prior package-manager experience needed.

Supported operating systems

  • macOS — fully supported (tested on macOS with Apple Silicon). Detects brew, and pip/npm when present.
  • Linux — fully supported. Detects apt/apt-get and runs apt-cache policy read-only.
  • Windows — run the winget commands from the cheat sheet in PowerShell, or run the scripts unmodified inside WSL.

Hardware requirements

Any computer made in roughly the last 15 years. The lab only reads information about installed software; it needs no minimum RAM, disk, or GPU.

Required software

  • bash (3.2 or newer — preinstalled on macOS and Linux).
  • Standard OS utilities only: command, uname, date, sed, grep, head.
  • Whatever package managers you already have — the lab installs none.

Free and open-source options

Everything here is free. Homebrew and apt are fully open-source, as are the packages in their main repositories; winget is free and first-party on Windows. The lab itself uses only bash and standard utilities. No account, API key, or purchase is required.

Installation

None. Copy this directory (or clone the repository) and you are ready:

cd labs/sections/computing-foundations/day-013-package-managers-homebrew-apt-and-winget

File structure

day-013-package-managers-homebrew-apt-and-winget/
├── README.md                              ← you are here
├── metadata.yml                           ← machine-readable lab metadata
├── starter/
│   ├── detect_pkg_manager.sh              ← YOUR working file (4 exercises)
│   └── package-manager-worksheet.md        ← worksheet for the practice assignment
├── examples/
│   ├── detect_pkg_manager.sh              ← completed reference detector
│   └── command-cheatsheet.md               ← brew / apt / winget task-by-task table
├── tests/
│   └── run_tests.sh                        ← automated checks (read-only, exits 0/1)
├── expected-output/
│   ├── sample-macos.txt                    ← real captured run (macOS, Apple Silicon)
│   └── FIELDS.md                           ← required fields on every platform
├── requirements/
│   └── README.md                           ← dependency statement (none beyond the OS)
├── troubleshooting.md
└── security.md

How to run

From this directory:

## 1. See the finished result first
bash examples/detect_pkg_manager.sh

## 2. Your task: complete the four exercises in the starter, then run it
bash starter/detect_pkg_manager.sh

## 3. Check your work
bash tests/run_tests.sh

What the commands do

  • bash examples/detect_pkg_manager.sh — runs the reference detector: it checks for brew, apt, apt-get, winget, pip, pip3, and npm with command -v, and for each one found prints its location and one read-only query (brew --version and brew list; apt-cache policy; winget --version; pip/pip3 --version; npm --version). It installs nothing.
  • bash starter/detect_pkg_manager.sh — the same skeleton with four read-only queries left as numbered exercises; each comment names the exact command to add.
  • bash tests/run_tests.sh — runs both scripts and checks they exit 0, print the report header/footer, and report either a found manager or a clear "none detected" message; it also statically scans both scripts to confirm they contain no sudo and no install/remove/upgrade commands.

Expected output

See expected-output/sample-macos.txt — a real captured run:

=== Package Manager Report ===
Generated on: 2026-07-12
Operating system kernel: Darwin

FOUND: brew -> /opt/homebrew/bin/brew
  read-only query: brew --version
    Homebrew 6.0.9
  read-only query: brew list | head
    abseil
    ada-url
    aom
    apr
    apr-util

FOUND: pip -> /opt/homebrew/opt/python@3.14/libexec/bin/pip
  read-only query: pip --version
    pip 25.2 from /opt/homebrew/lib/python3.14/site-packages/pip (python 3.14)

FOUND: npm -> /opt/homebrew/bin/npm
  read-only query: npm --version
    11.17.0

----------------------------------------
Detected 3 package manager(s) above (read-only).
Nothing was installed, removed, or upgraded.
=== End of report ===

Your values will differ — that is the point. On Debian/Ubuntu the report finds apt/apt-get and shows apt-cache policy output instead; expected-output/FIELDS.md lists exactly which fields must appear on every platform.

Validation steps

  1. Run bash examples/detect_pkg_manager.sh — it must exit without errors and print the header and footer.
  2. Confirm it either lists at least one FOUND: manager or states clearly that none were detected.
  3. Complete the four exercises in starter/detect_pkg_manager.sh and run it — the placeholder (exercise N: ...) lines should be replaced by real read-only query output.
  4. Confirm you installed and removed nothing.
  5. Run the tests (next section) — all checks must pass.

Tests

bash tests/run_tests.sh

Expected final line: 14 checks, 0 failure(s). The command exits 0 on success and non-zero on any failure, so it can run in CI. The suite includes a static safety scan that fails if either script contains sudo or an install/remove/upgrade command — this lab is strictly read-only.

Cleanup

Nothing to clean up: the scripts only read information and write nothing outside their own console output. To reset your work, restore the starter from git: git checkout -- starter/detect_pkg_manager.sh.

Troubleshooting

See troubleshooting.md for the full list (no managers found, brew/pip absent, Windows/WSL notes, safety-scan failures).

Security notes

See security.md. Short version: the scripts make no network calls, need no elevated privileges, and install nothing — but real installs elsewhere do change your system and sometimes need sudo, so run those deliberately and only from trusted repositories.

Extension exercises

  1. Show the dependency tree of a package you have: brew deps --tree wget (macOS) or apt-cache depends wget (Linux). Count how many packages one small tool rests on.
  2. Add a pip3 list (Python) or npm ls -g --depth=0 (global Node packages) read-only query to the detector and note how a language manager's inventory differs from a system manager's.
  3. Using the cheat sheet, write out (do not run) the full sequence you would use on your OS to search for, install, list, and then remove a package — the complete life cycle.
  • Previous day: Day 12 — Shell Scripting: Variables, Loops, and Conditionals (labs/sections/computing-foundations/day-012-shell-scripting-variables-loops-and-conditionals/).
  • Next day: Day 14 — Automating Tasks with Shell Scripts and cron (labs/sections/computing-foundations/day-014-automating-tasks-with-shell-scripts-and/, to be written).

Expected output

FIELDS.md

# Expected report fields (all platforms)

A correct run of `detect_pkg_manager.sh` prints, in order:

1. `=== Package Manager Report ===`
2. `Generated on: YYYY-MM-DD`
3. `Operating system kernel: <Darwin | Linux | ...>`
4. Zero or more `FOUND: <manager> -> <path>` blocks, each followed by one or
   more indented `read-only query:` lines and their output.
5. A summary line: either `Detected <n> package manager(s) above ...` or
   `No package managers detected on this machine.`
6. `=== End of report ===`

The script always exits `0`.

## What differs by platform

- **macOS:** typically finds `brew` (if installed) and, when Python/Node are
  present, `pip`/`pip3` and `npm`. Never finds `apt`, `apt-get`, or `winget`.
  `sample-macos.txt` in this directory is a real captured run (macOS, Apple
  Silicon, 2026-07-12): it found `brew`, `pip`, and `npm`.
- **Debian/Ubuntu Linux:** finds `apt` and `apt-get`; the read-only query is
  `apt-cache policy`, which lists the configured repositories without changing
  anything. May also find `pip3`/`npm`. Never finds `brew` (unless separately
  installed) or `winget`.
- **Windows (PowerShell/WSL):** in native PowerShell you would check
  `winget --version` and `winget list`. Under WSL the script runs as on Linux.

No matter the platform, the script installs, removes, and upgrades **nothing**.
A run that finds no managers at all is a valid outcome and still exits `0`.

sample-macos.txt

=== Package Manager Report ===
Generated on: 2026-07-12
Operating system kernel: Darwin

FOUND: brew -> /opt/homebrew/bin/brew
  read-only query: brew --version
    Homebrew 6.0.9
  read-only query: brew list | head
    abseil
    ada-url
    aom
    apr
    apr-util

FOUND: pip -> /opt/homebrew/opt/python@3.14/libexec/bin/pip
  read-only query: pip --version
    pip 25.2 from /opt/homebrew/lib/python3.14/site-packages/pip (python 3.14)

FOUND: npm -> /opt/homebrew/bin/npm
  read-only query: npm --version
    11.17.0

----------------------------------------
Detected 3 package manager(s) above (read-only).
Nothing was installed, removed, or upgraded.
=== End of report ===

Source files

examples/command-cheatsheet.md (2072 bytes)
# Package Manager Command Cheat Sheet

The same task, three managers. Learn the model once and read across the row.
Commands that change the system are marked; this lab runs only the read-only
ones (search, list, version, info).

| Task | Homebrew (macOS) | apt (Debian/Ubuntu) | winget (Windows) |
| --- | --- | --- | --- |
| Show the manager's version | `brew --version` | `apt --version` | `winget --version` |
| Refresh the repository index | `brew update` | `sudo apt update` | (automatic) |
| Search for a package | `brew search wget` | `apt search wget` | `winget search wget` |
| Show info about a package | `brew info wget` | `apt show wget` | `winget show wget` |
| **Install** a package | `brew install wget` | `sudo apt install wget` | `winget install wget` |
| List installed packages | `brew list` | `apt list --installed` | `winget list` |
| **Upgrade** everything | `brew upgrade` | `sudo apt upgrade` | `winget upgrade --all` |
| **Remove** a package | `brew uninstall wget` | `sudo apt remove wget` | `winget uninstall wget` |
| Show a package's dependencies | `brew deps --tree wget` | `apt-cache depends wget` | (see `winget show`) |

## Notes

- **sudo:** apt installs system-wide, so its *changing* commands (install,
  upgrade, remove) need administrator rights via `sudo`. Homebrew installs into
  your user space and normally does not. On Windows, winget may prompt for
  elevation for some installers.
- **update vs upgrade (apt):** `apt update` refreshes the local copy of the
  repository index; `apt upgrade` actually moves installed packages to newer
  versions. They are different commands — do not confuse them.
- **Read-only vs changing:** version, search, info, list, and dependency
  queries change nothing and are safe to explore with. Install, upgrade, and
  remove change your system — run them deliberately.
- **Language managers:** `pip install <name>` (Python) and `npm install <name>`
  (Node.js) follow the same model one layer down, for code libraries rather
  than whole applications. You will meet them in a later lesson.
examples/detect_pkg_manager.sh (2279 bytes)
#!/usr/bin/env bash
# Day 013 lab — completed reference implementation.
#
# Detects which package managers are available on THIS machine and runs one
# safe, READ-ONLY query against each one it finds. It never installs, removes,
# or upgrades anything, never uses sudo, and needs no network connection.
#
# Detected: brew, apt, apt-get, winget, pip, pip3, npm.
set -u

echo "=== Package Manager Report ==="
echo "Generated on: $(date '+%Y-%m-%d')"
echo "Operating system kernel: $(uname -s)"
echo

found=0

# have <name> -> prints the resolved path if the command exists, else nothing.
have() {
  command -v "$1" 2>/dev/null
}

# header <name> -> if <name> exists, print the FOUND line and return 0.
header() {
  local path
  path="$(have "$1")"
  if [ -n "${path}" ]; then
    found=$((found + 1))
    echo "FOUND: $1 -> ${path}"
    return 0
  fi
  return 1
}

# query <description> <command...> -> label and run a read-only query, indented.
query() {
  local desc="$1"
  shift
  echo "  read-only query: ${desc}"
  "$@" 2>&1 | sed 's/^/    /' | head -n 6
}

# --- System package managers -------------------------------------------
if header brew; then
  query "brew --version" brew --version
  query "brew list | head" bash -c 'brew list 2>/dev/null | head -n 5'
  echo
fi

if header apt; then
  query "apt-cache policy (configured repositories)" apt-cache policy
  echo
elif header apt-get; then
  query "apt-cache policy (configured repositories)" apt-cache policy
  echo
fi

if header winget; then
  query "winget --version" winget --version
  echo
fi

# --- Language package managers -----------------------------------------
if header pip; then
  query "pip --version" pip --version
  echo
elif header pip3; then
  query "pip3 --version" pip3 --version
  echo
fi

if header npm; then
  query "npm --version" npm --version
  echo
fi

echo "----------------------------------------"
if [ "${found}" -eq 0 ]; then
  echo "No package managers detected on this machine."
  echo "That is a valid result: on macOS you may not have Homebrew yet; on"
  echo "minimal Linux, apt should be present. Nothing was installed."
else
  echo "Detected ${found} package manager(s) above (read-only)."
  echo "Nothing was installed, removed, or upgraded."
fi
echo "=== End of report ==="
metadata.yml (634 bytes)
lesson_id: D013
day: 13
kind: command-line-inspection
languages: [bash]
setup_commands:
  - cd labs/sections/computing-foundations/day-013-package-managers-homebrew-apt-and-winget
run_commands:
  - bash examples/detect_pkg_manager.sh
  - bash starter/detect_pkg_manager.sh
test_commands:
  - bash tests/run_tests.sh
cleanup_commands:
  - 'git checkout -- starter/detect_pkg_manager.sh  # optional: reset your work'
requires_network: false
requires_api_key: false
estimated_minutes: 30
last_executed: '2026-07-12'
executed_on: 'macOS (Apple Silicon), bash tests/run_tests.sh → 14 checks, 0 failure(s); detector found brew, pip, npm'
requirements/README.md (765 bytes)
# Dependencies — Day 013 lab

**None beyond a POSIX shell.** This lab intentionally has zero installable
dependencies — fitting, since its whole subject is installing software:

- `bash` ≥ 3.2 (preinstalled on macOS and every mainstream Linux distribution)
- Standard OS utilities: `command`, `uname`, `date`, `sed`, `grep`, `head` —
  all part of the base system.

The lab only *detects and inspects* the package managers you already have; it
installs nothing and needs no network connection. If a manager the script looks
for (brew, apt, apt-get, winget, pip, pip3, npm) is absent, that is simply
reported and is a valid result — you do not need to install anything to
complete the lab.

There is deliberately no `requirements.txt`/`package.json` here.
starter/detect_pkg_manager.sh (2554 bytes)
#!/usr/bin/env bash
# Day 013 lab — YOUR working file.
#
# This starter already prints the report header and detects package managers
# for you. Your task is the four numbered exercises below: each asks you to add
# ONE safe, READ-ONLY query for a manager. Every command you add must be
# read-only — no install, no remove, no upgrade, no sudo, no network required.
#
# The completed reference version is examples/detect_pkg_manager.sh — try the
# exercises yourself first, then compare.
set -u

echo "=== Package Manager Report ==="
echo "Generated on: $(date '+%Y-%m-%d')"
echo "Operating system kernel: $(uname -s)"
echo

found=0

have() { command -v "$1" 2>/dev/null; }

header() {
  local path
  path="$(have "$1")"
  if [ -n "${path}" ]; then
    found=$((found + 1))
    echo "FOUND: $1 -> ${path}"
    return 0
  fi
  return 1
}

query() {
  local desc="$1"; shift
  echo "  read-only query: ${desc}"
  "$@" 2>&1 | sed 's/^/    /' | head -n 6
}

# --- System package managers -------------------------------------------
if header brew; then
  # Exercise 1 (Homebrew): add a read-only query that prints Homebrew's version.
  #   Use exactly:  query "brew --version" brew --version
  echo "  (exercise 1: add the brew --version query here)"
  echo
fi

if header apt || header apt-get; then
  # Exercise 2 (apt): add a read-only query that shows the configured repositories
  # WITHOUT changing anything. Use exactly:
  #   query "apt-cache policy" apt-cache policy
  echo "  (exercise 2: add the apt-cache policy query here)"
  echo
fi

if header winget; then
  # Exercise 3 (winget): add a read-only query that prints winget's version.
  #   Use exactly:  query "winget --version" winget --version
  echo "  (exercise 3: add the winget --version query here)"
  echo
fi

# --- Language package managers -----------------------------------------
if header pip || header pip3; then
  # Exercise 4 (pip): add a read-only query that prints pip's version. Use the
  # name that exists on your machine, for example:
  #   query "pip3 --version" pip3 --version
  echo "  (exercise 4: add the pip/pip3 --version query here)"
  echo
fi

if header npm; then
  query "npm --version" npm --version
  echo
fi

echo "----------------------------------------"
if [ "${found}" -eq 0 ]; then
  echo "No package managers detected on this machine."
  echo "That is a valid result: nothing was installed."
else
  echo "Detected ${found} package manager(s) above (read-only)."
  echo "Nothing was installed, removed, or upgraded."
fi
echo "=== End of report ==="
starter/package-manager-worksheet.md (1638 bytes)
# Package Manager Worksheet — Day 013

Fill this in for **your** machine using the read-only commands from the lab.
Do **not** run any install command — this worksheet only records what you find
and what you *would* type.

## 1. Which package managers does your machine have?

Run `command -v brew apt apt-get winget pip pip3 npm` (or `bash examples/detect_pkg_manager.sh`)
and list every one it found:

- Package manager(s) found: `__________________________`

## 2. Your default system package manager

Based on your operating system, which is the *default* system package manager?
(macOS → Homebrew; Debian/Ubuntu → apt; Windows → winget.)

- My operating system: `__________________________`
- My default system package manager: `__________________________`

## 3. How you would install `wget` (write it — do NOT run it)

Write the exact command you *would* use to install the `wget` package on your
operating system. Do not run it.

- Install command for `wget`: `__________________________`

  (macOS: `brew install wget` · Debian/Ubuntu: `sudo apt install wget` · Windows: `winget install wget`)

## 4. One package you already have installed

List one package that is already installed, from `brew list`,
`apt list --installed`, or `winget list`:

- A package I already have: `__________________________`

## 5. Short reflection (4–6 sentences)

In your own words, explain why installing that one package with a package
manager was safer and more repeatable than downloading it by hand. Name
**dependency resolution**, **integrity verification**, and **record-keeping**
explicitly.

```
Your paragraph here:



```
tests/run_tests.sh (2764 bytes)
#!/usr/bin/env bash
# Tests for the Day 013 lab. Run from the lab directory:
#   bash tests/run_tests.sh
#
# Verifies that the detector runs, produces a well-formed report that either
# lists at least one manager or states clearly that none were detected, and
# exits 0. It ALSO statically checks that the scripts contain no install,
# remove, upgrade, or sudo commands — this lab is strictly read-only.
set -u

lab_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
failures=0
checks=0

check() {
  local label="$1" ok="$2"
  checks=$((checks + 1))
  if [ "${ok}" = "yes" ]; then
    echo "  ok: ${label}"
  else
    echo "  FAIL: ${label}"
    failures=$((failures + 1))
  fi
}

run_report_checks() {
  local script="$1" output rc
  echo "Testing ${script} ..."
  output="$(bash "${script}" 2>&1)"
  rc=$?
  [ "${rc}" -eq 0 ] && check "script exits 0" "yes" || check "script exits 0" "no"
  echo "${output}" | grep -q '^=== Package Manager Report ===$' \
    && check "prints report header" "yes" || check "prints report header" "no"
  echo "${output}" | grep -q '^=== End of report ===$' \
    && check "prints report footer" "yes" || check "prints report footer" "no"
  echo "${output}" | grep -q '^Operating system kernel: ' \
    && check "prints OS kernel line" "yes" || check "prints OS kernel line" "no"
  # Either at least one manager was found, or a clear "none detected" message.
  if echo "${output}" | grep -q '^FOUND: ' \
    || echo "${output}" | grep -q 'No package managers detected'; then
    check "reports a manager OR a clear 'none detected' message" "yes"
  else
    check "reports a manager OR a clear 'none detected' message" "no"
  fi
}

# Static safety check: no mutating or privileged commands anywhere in the lab
# scripts. This lab must never install, remove, upgrade, or use sudo.
check_read_only() {
  local script="$1"
  echo "Safety-scanning ${script} ..."
  # Strip comment lines, then look for dangerous verbs as commands.
  local body
  body="$(grep -v '^[[:space:]]*#' "${script}")"
  if echo "${body}" | grep -Eq '\b(sudo)\b'; then
    check "no sudo in ${script##*/}" "no"
  else
    check "no sudo in ${script##*/}" "yes"
  fi
  if echo "${body}" | grep -Eq '(brew|apt|apt-get|winget|pip|pip3|npm)[[:space:]]+(install|remove|uninstall|upgrade|update)\b'; then
    check "no install/remove/upgrade in ${script##*/}" "no"
  else
    check "no install/remove/upgrade in ${script##*/}" "yes"
  fi
}

run_report_checks "${lab_dir}/examples/detect_pkg_manager.sh"
run_report_checks "${lab_dir}/starter/detect_pkg_manager.sh"
check_read_only "${lab_dir}/examples/detect_pkg_manager.sh"
check_read_only "${lab_dir}/starter/detect_pkg_manager.sh"

echo
echo "${checks} checks, ${failures} failure(s)."
[ "${failures}" -eq 0 ]

Troubleshooting

Troubleshooting — Day 013 lab

The script prints no FOUND: lines at all

None of the managers it looks for are on your PATH. This is a valid result, and the script says so and still exits 0. On macOS you may simply not have Homebrew installed yet; on a minimal Linux system, try command -v apt on its own to confirm apt is present.

brew: command not found

Homebrew is not installed, or its directory is not on your PATH. This lab never installs anything, so just record that brew is absent and inspect whichever manager you do have. (If you later choose to install Homebrew, follow the official instructions at the Homebrew documentation — outside this lab.)

pip is missing but pip3 is present (or vice versa)

On many systems the Python package manager is named pip3. The detector checks both and reports whichever exists; treat a hit on either as "pip is available."

apt-cache policy prints a lot of output

That command lists every configured repository and pinning rule. The script indents it and shows only the first few lines with head — that is expected. apt-cache is read-only and changes nothing.

Permission denied when running the script

You do not need to make it executable — run it through bash explicitly: bash starter/detect_pkg_manager.sh. If you prefer ./starter/..., first run chmod +x starter/detect_pkg_manager.sh.

I am on Windows and bash is not recognized

Use WSL (wsl --install, then open Ubuntu and follow the Linux path), or skip the script and run the winget commands from the cheat sheet directly in PowerShell (winget --version, winget list), filling the worksheet manually.

The tests report a safety failure

The test suite statically scans both scripts for sudo and for install/remove/upgrade commands and fails if it finds any. If you edited the starter and added a changing command, remove it — every query in this lab must be read-only.

Security notes

Security notes — Day 013 lab

  • What the scripts do: detect which package managers exist (command -v ...) and run read-only queries (--version, brew list, apt-cache policy). They make no network connections, install nothing, remove nothing, upgrade nothing, write no files outside their own console output, and never use sudo. The test suite statically enforces this.
  • Real installs need care — and sometimes sudo. Outside this lab, installing software does change your system, and on Linux (apt) the changing commands require administrator rights via sudo. Treat sudo as a deliberate act: never sudo a command you have not read and understood, and prefer official repositories over third-party ones.
  • Trust the source. A package manager verifies that a download matches what the repository published, but it cannot vouch for software you point it at from an untrusted repository. Read package names carefully — a name that is a near-miss of a popular tool ("typosquatting") is a known trick — and prefer your operating system's official repository.
  • Privacy: installing packages tells the repository's servers which software a machine requested. The set of packages you have installed is mildly revealing; this lab only lists it locally and sends nothing anywhere.
  • Reading before running: both scripts are short and commented — read them first. Running unread shell scripts is one of the most common ways developers get compromised; every script in this course is small enough to read before you run it.