The Unix Underneath: Terminal, zsh & the BSD Toolset
macOS
Chapter 4 · The Unix Underneath: Terminal, zsh & the BSD Toolset
Chapter 1 proved Darwin was real with a single command, and Chapter 3 mentioned Terminal as the escape hatch for whatever System Settings can't reach. This chapter finally opens Terminal properly and stays there — going hands-on with the default shell and the command-line toolset that actually implements the BSD heritage Chapter 1 only described. Cmd+Space, type Terminal, press Return, and everything below can be followed along directly.
zsh: The Default Shell Since Catalina
macOS Catalina (10.15, 2019) switched the default shell from bash to zsh (the "Z shell"). The reason is a genuinely specific one, not a stylistic preference: bash versions from 4.0 onward are licensed under the GPLv3, and Apple's legal position avoids shipping GPLv3 software in macOS. macOS still bundles bash today — but it's a frozen, ancient bash 3.2, the last release under the older GPLv2, kept only for backward compatibility with scripts that explicitly call it. zsh, by contrast, is BSD-licensed, which is exactly why it was free to become the new default instead.
zsh is close enough to bash that most everyday interactive use and simple scripts carry over directly — but Bash Scripting Fundamentals' own material doesn't automatically transfer without at least one real gotcha worth knowing up front:
A bash script relying on zero-indexed arrays will silently misbehave if run under zsh without a shebang explicitly pinning it to #!/bin/bash — worth confirming which interpreter a script actually declares, rather than assuming Terminal's default shell is bash just because it always used to be.
The BSD Toolset: More Gotchas Than Just ls
Chapter 1 showed ls --sort=time failing on macOS because its BSD-derived ls never implemented that GNU long-option spelling. That was one example of a much wider pattern — the same command names, shipped by two different Unix lineages, quietly disagreeing on flags. Two more, both genuinely sharp enough to cause real damage if missed:
A third, quieter one: GNU's readlink -f (resolve a path to its final, absolute, symlink-free form) has no BSD equivalent on macOS at all by default — scripts relying on it need a workaround, or a substitute tool entirely.
g — gsed, gdate, greadlink — so a script that genuinely needs GNU behavior can call the g-prefixed version explicitly, rather than fighting BSD's own flags or hoping they happen to match.
sed -i example above isn't a hypothetical — a tutorial or Stack Overflow answer written against GNU sed, pasted directly into macOS's Terminal, will either error out confusingly or, worse, silently do something other than what was intended, because BSD sed parses the exact same argument list differently. Any one-liner copied from a source that doesn't explicitly say "tested on macOS" is worth reading once before running, not just trusting because the command name matches.
ls against GNU's. This chapter is the same finding a third time, with sed, date, and readlink as the evidence: whenever more than one Unix lineage is involved, matching command names are never a guarantee of matching command behavior. Three separate courses, three separate concrete examples, one single underlying rule.
Where This Course Is Headed
Homebrew as the community package-manager answer this chapter's own g-prefixed tools already previewed, the application model (app bundles, code signing, Gatekeeper), APFS, the built-in security stack, Time Machine and Recovery, networking and sharing, everyday troubleshooting tools, and a capstone setting up and securing a complete new Mac end to end.
Hands-On Exercises
Explain the specific licensing reason Apple switched macOS's default shell from bash to zsh in Catalina, and what version of bash macOS still bundles today, and why that particular version was chosen rather than a newer one.
📄 View solutionWalk through exactly what happens if you run sed -i 's/foo/bar/' file.txt on macOS's BSD sed, expecting GNU sed's behavior. Which argument gets misinterpreted as what, and how would you fix the command to behave correctly on macOS?
This chapter calls its own finding-box "the same lesson, now a third time on this site." Name all three courses/chapters involved and the specific command-line example each one used to demonstrate it.
📄 View solutionChapter 4 Quick Reference
- zsh — macOS's default shell since Catalina (2019); BSD-licensed, unlike GPLv3-licensed modern bash
- bash 3.2 — the old, frozen version still bundled with macOS, kept only for backward compatibility
- Array indexing gotcha — zsh is 1-indexed by default; bash (and Bash Scripting Fundamentals' own examples) is 0-indexed
sed -i— BSD requires an explicit suffix argument (-i ''for "no backup"); GNU's is optionaldate— GNU's-dparses flexible strings; BSD's-vadjusts by value, a different flag entirelyreadlink -f— no BSD equivalent by default; Homebrew'sg-prefixed coreutils (greadlink, etc.) are the common fix- This chapter's own throughline: matching command names never guarantee matching command behavior across Unix lineages — now shown three times across this site
- Next chapter: Package Management — Homebrew