2026年7月16日

macOS 命令行从零指南

一份写给技术新手的命令行手册:从终端到底是什么讲起,最终覆盖 Git 和 Apple 开发工具链。

本文的记忆方法:几乎每个命令的名字都不是乱起的——它们是有源头的缩写、有出处的比喻(第 0 章会讲这背后的历史成因)。命令名大致分三类——

  1. 缩写型pwd = print working directory(打印工作目录)。展开缩写,就永远不会忘。
  2. 合成型mkdir = make directory(创建目录)。两个词各取一段拼起来。
  3. 比喻型:Git 的 stash(藏匿处)、macOS 的 caffeinate(咖啡因,喝了不睡觉)。理解比喻,命令就有了画面感。

后文每个命令都会标注它的命名来源。先问"这是什么的缩写",再记用法——这是学命令行最省力的路径。

0. 认识终端:这一切的起点

图形界面这么好用,为什么还要命令行

因为两者擅长的事不同。图形界面擅长"看"和"探索";命令行擅长四件图形界面做不好的事:

  1. 精确。"找出这个文件夹里所有大于 100MB、三个月没动过的文件"——鼠标点不出这句话,命令行一行完成。
  2. 组合。命令可以像积木一样拼接(第 3 章的管道),几十个小工具能组合出无穷种能力。
  3. 自动化。命令能存成脚本反复执行、定时执行;而你昨天在 Finder 里的一通点击无法"再放一遍"。
  4. 无处不在。服务器没有图形界面,部署网站、远程排障时命令行是唯一入口。

对开发者还有一层:Git、构建工具、包管理器这些核心工具,本体全是命令行程序——图形工具只是给它们套的壳。学会本体,才不被壳限制。

一点历史:你的 Mac 是 Unix 的后代

把时间拨回 1969 年,贝尔实验室。两位工程师 Ken Thompson 和 Dennis Ritchie 在一台闲置的小型机上写出了一个操作系统,取名 Unix。今天你 Mac 上的大部分命令,就诞生在那几年。

那个年代没有显示器。人与计算机对话用的是电传打字机(teletype,缩写 TTY):一台带键盘的打印机,你敲一行字,它通过电缆发给主机;主机的回复直接打印在纸上。这台打字机位于通信线路的末端,所以被叫作 terminal——"终端",线的尽头。

这个词沿用至今。你打开的 Terminal.app 本质是那台打字机的软件仿真:一行行文本、一问一答的交互方式,都是从纸带时代原样继承的。(macOS 里它的血统随处可见:终端设备至今仍叫 tty,ps 命令的输出里就有一列 TTY。)

再看家谱。Unix 后来开枝散叶,其中一支经加州大学伯克利分校改良(BSD),被乔布斯离开苹果后创办的 NeXT 公司选作系统基础;1997 年苹果收购 NeXT,这套系统演化成了 Mac OS X——也就是今天的 macOS。

text
1969  Unix(贝尔实验室)
 ├── 1978  BSD(伯克利改良版)
 │      └── 1989  NeXTSTEP(乔布斯的 NeXT 公司)
 │             └── 2001  Mac OS X ——→ 今天的 macOS
 └── 1991  Linux(Linus Torvalds 参照 Unix 从零重写的自由版本)

两个对你有实际意义的结论:

  • macOS 是"根正苗红"的 UNIX(通过了官方认证),命令行血统五十多年,不是后来补装的功能。
  • Linux 是 Unix 的"神似"重写,绝大多数命令与 macOS 相通。本文学的东西,在你将来租的任何一台 Linux 服务器上原样可用——学一次,Mac 和服务器通吃。

为什么命令名都这么"难懂"

知道了历史,就能回答这个新手最大的困惑。电传打字机每秒只能传约 10 个字符,每敲一个键都要等它咔哒一声打在纸上——键入是有真实成本的。于是先驱们把命令压缩到极限:list 压成 ls,change directory 压成 cd,copy 压成 cp

这不是故意神秘,是那个年代的"省流量"。有个流传很广的问答:多年后有人问 Ken Thompson,如果重新设计 Unix 会改什么,他答:"我会把 creat 拼成 create。"——当年连一个字母 e 都要省下来。

所以,这些短命令都是有源头的压缩包。本文的方法就是帮你把每个压缩包"解压"回原文——理解了展开形式,缩写自然就记住了。

终端和 Shell 是两回事

  • 终端(Terminal.app):那个黑色(或白色)的窗口。它只负责显示文字和接收键盘输入,本身不理解任何命令——正如当年的打字机不理解你打的字。
  • Shell:窗口背后真正干活的解释器程序。你输入命令,它解析、执行、把结果送回终端显示。macOS 默认的 Shell 是 zsh
命名
Shell 意为"外壳"——操作系统真正的核心叫 kernel(果核、内核),Shell 包裹在核外面,是人与内核对话的中间层。壳与核,一对贯穿始终的比喻。zsh = Z Shell,名字取自作者在普林斯顿求学时一位助教 Zhong Shao 的登录账号 "zsh"。它的前辈 bash = Bourne-Again SHell,一个双关:既是"Bourne Shell 的再生"(致敬初代作者 Stephen Bourne),又谐音 "born again"。

打开方式:Cmd + 空格 呼出 Spotlight,输入 Terminal 回车。你会看到一行提示符(prompt):

text
jerry@MacBook-Pro ~ %
│     │           │ └── %:zsh 的提示符,意思是"轮到你说话了"(bash 用 $,root 用 #)
│     │           └──── 当前位置(~ 表示你的主目录,下一章展开)
│     └──────────────── 机器名
└────────────────────── 用户名

后文示例里,你要输入的只是提示符之后的部分。

一条命令的解剖

bash
ls -l -a ~/Desktop
│  │  │  └── 参数:对什么操作(这里是桌面目录)
│  │  └───── 选项:调整行为
│  └──────── 选项:调整行为
└─────────── 命令:做什么

结构规则很简单,几乎所有命令都循此形:

bash
ls -l -a             # 短选项:一个横线 + 单字母
ls -la               # 短选项可以合并
git log --oneline    # 长选项:两个横线 + 完整单词,常见于现代工具(git、brew、swift)

一个 macOS 特色要提前知道:系统自带的老牌 Unix 命令(ls、cp、rm 等源自 BSD)大多只认短选项——ls --all 会直接报错。长选项是 GNU 和现代工具的风格。

选项字母也有约定俗成的惯例。注意:是惯例,不是标准——同一个字母在不同命令里可能含义不同,拿不准时以 man 为准。但高频惯例仍值得先记:

选项通常的全称含义
-aall全部(含隐藏项)
-llong长格式(详细信息)
-hhuman-readable人类可读(把 1048576 显示成 1M);单独使用时常是 help
-Rrecursive递归(连同子目录一起)。小写 -r 在 cp/rm 里也是递归,但在 ls 里是 reverse 反序——"惯例有例外"的典型
-fforce强制,不再确认
-vverbose啰嗦模式(输出详细过程);有时是 version
-iinteractive交互式(做之前问一下)
-nnumber数量

遇到不会的命令,三招自救

bash
man ls        # manual(手册):官方完整文档,按 q 退出,/ 搜索
ls --help     # 多数命令支持,输出简短用法
tldr ls       # 第三方工具(见第 10 章):只给最常用的几个例子,新手首选
命名
man = manual(手册)。tldr = too long; didn't read(太长不看)——名字即态度:man 手册太长,我只要例子。

键盘生存技巧

text
Tab          自动补全命令和路径(最重要的键,没有之一)
↑ / ↓        翻阅历史命令
Ctrl + C     强行终止当前命令
Ctrl + A / E 光标跳到行首 / 行尾
Cmd + K      清屏(或输入 clear 命令)

新手安全守则

  1. 命令行删除不进废纸篓rm 删掉就是删掉了。
  2. 看到 `sudo` 提高警惕——它意味着以最高权限执行,系统不再拦你。
  3. 不要粘贴运行看不懂的命令,尤其是含 sudorm -rfcurl ... | sh 的。
  4. 拿不准时,先用 mantldr 查一下再敲回车。

1. 移动:你在哪里,有什么,去哪里

先弄清三个基础词:文件、目录、文件系统

  • 文件(file):一段有名字的数据。一份代码、一张图片、一个 app,在系统眼里都是文件。
  • 目录(directory):装文件(和其他目录)的容器——就是图形界面里的"文件夹",同一个东西的两个名字。
命名
directory 本义是"名录、通讯录"。这个名字暴露了它的本质:目录其实是一张清单,记录着"名字 → 数据在磁盘上的位置"的对应关系,像通讯录记录"人名 → 电话"。中文译作"目录",取的也是书籍目录的同一个比喻。"文件夹"(folder)则是后来图形界面发明的办公室比喻——牛皮纸文件夹。
  • 文件系统(file system):所有目录层层嵌套,构成一棵倒挂的树。树根是 /,称为根目录(root)。你电脑上的每个文件,都能从根出发一路走到:
text
/                      ← 根:一切从这里长出
├── Applications       ← 所有 app 住这里
├── System             ← macOS 本体(别动它)
└── Users              ← 所有用户
    └── jerry          ← 你的主目录,简写 ~,你的文件都在这棵子树上
        ├── Desktop
        ├── Documents
        └── Projects
            └── MyApp

在 Finder 里双击进入文件夹,和在命令行里 cd 进目录,走的是同一棵树——只是一个用鼠标,一个用键盘。

最后一个概念:命令行没有"当前窗口",但有个等价物——工作目录(working directory),即你此刻"站"在树上的哪个节点。所有相对操作都以它为基准。

生存三命令

新手先记住这三个命令,它们回答三个问题:

pwd我在哪里?

命名
print working directory,打印工作目录。
bash
pwd
# 输出:/Users/jerry/Projects

ls这里有什么?

命名
list,列出。Unix 早期为了少敲键盘,把单词压缩到两个字母。
bash
ls              # 列出当前目录
ls -l           # long:详细模式(权限、大小、修改时间)
ls -a           # all:包括隐藏文件
ls -la          # 合并:详细 + 全部
ls -lh          # 详细 + 人类可读的文件大小
ls ~/Desktop    # 也可以看别的目录,不用先走过去

关于隐藏文件:名字以 . 开头的文件默认不显示(如 .gitignore.zshrc)。这是 Unix 的约定:配置类文件藏起来,不打扰日常视线。它们并不神秘——Finder 里按 Cmd + Shift + . 同样能看到。

ls -l 的输出信息密度很高,学会读它,一行顶 Finder 一个弹窗:

text
-rw-r--r--  1 jerry staff  1024  7 14 10:30 README.md
│           │ │     │      │     │          └── 文件名
│           │ │     │      │     └── 最后修改时间
│           │ │     │      └── 大小(字节;加 -h 会显示成 1K)
│           │ │     └── 所属群组
│           │ └── 所有者
│           └── 链接数(新手可忽略)
└── 类型与权限:首字符 - 是文件、d 是目录(directory);
    后面 rwx = read/write/execute 读/写/执行(第 4 章 chmod 再会)

cd去别的地方

命名
change directory,切换目录。
bash
cd Projects         # 进入当前目录下的 Projects
cd /Users/jerry     # 用绝对路径直达
cd ..               # 回上一级
cd ~                # 回家(用户主目录)
cd                  # 不带参数也是回家
cd -                # 回到上一次所在的目录(来回横跳很好用)

路径的记号系统

记号含义记忆
/根目录(整个文件系统的起点)root
~你的主目录 /Users/你的用户名home,波浪号像屋顶
.当前目录一个点 = 这里
..上一级目录两个点 = 再往上
-上一次所在目录减号 = 退回

/ 开头的是绝对路径(从根出发,任何位置都有效);不以 / 开头的是相对路径(从当前位置出发)。

练习cd ~ 回家 → ls -la 看看家里有什么 → cd Desktoppwd 确认位置 → cd - 跳回去。


2. 文件操作:创建、复制、移动、删除

mkdir创建目录

命名
make directory,创建目录。
bash
mkdir Notes                  # 创建一个目录
mkdir -p App/Sources/Views   # -p = parents:父目录不存在就一并创建

touch创建空文件

命名
本义"触摸"。它的原始职责是更新文件的修改时间戳(摸一下,文件就"新"了);文件不存在时会顺手创建一个空的——后者反而成了最常见用法。
bash
touch README.md

cp复制

命名
copy。
bash
cp a.txt b.txt               # 复制文件并命名
cp a.txt ~/Desktop/          # 复制到别处
cp -R Sources/ backup/       # -R = recursive:复制目录必须递归

mv移动(兼职:重命名)

命名
move。在文件系统看来,"重命名"就是"移动到同目录的新名字",所以一个命令干两件事。
bash
mv draft.md final.md         # 重命名
mv final.md ~/Documents/     # 移动

rm删除(危险区)

命名
remove。
bash
rm a.txt                     # 删除文件
rm -r build/                 # recursive:删除目录
rm -rf build/                # + force:不问任何问题直接删。敲回车前务必看清路径
rmdir empty_dir              # remove directory:只能删空目录(所以用得少)

再强调一次:`rm` 不经过废纸篓。 rm -rf 后面跟错路径是命令行世界最经典的事故。

ln -s软链接(快捷方式)

命名
link;-s = symbolic(符号的),即"软"链接,类似 Finder 的替身。
bash
ln -s /path/to/real/folder ~/shortcut

tar / zip打包与压缩

命名
tar = tape archive,磁带归档——名字暴露年龄:它诞生时的工作是把一堆文件打包后写入磁带做备份。磁带早已退役,命令活到了今天。zip 即"拉链",拉上就把东西合在一起。
bash
tar -czf backup.tar.gz Sources/    # 打包并压缩(c=create 创建 z=gzip 压缩 f=file 指定文件名)
tar -xzf backup.tar.gz             # 解包(x = extract 抽出)
tar -tzf backup.tar.gz             # 只列出内容不解压(t = list table)

zip -r archive.zip folder/         # zip 格式,跨平台分享更通用
unzip archive.zip -d output/       # -d = destination 解压到指定目录

tar 的选项组合像咒语,其实只需记两句:czf 压,xzf 解——create 和 extract 的区别而已。

open从终端回到图形世界(macOS 特有)

命名
直白的"打开"。它是终端和 Finder/App 之间的传送门,Linux 上没有这个待遇。
bash
open .                        # 用 Finder 打开当前目录(每天都会用)
open MyApp.xcodeproj          # 用默认 app(Xcode)打开
open -a Safari https://apple.com   # -a = application:指定用哪个 app
open -R file.swift            # -R = reveal:在 Finder 中定位这个文件

3. 查看与搜索:读文件、找内容、连接命令

cat一口气打印整个文件

命名
concatenate(拼接)。它的本职是把多个文件首尾相连输出,cat 单个文件 只是这个能力的副产品,却成了日常用法。
bash
cat notes.txt
cat a.txt b.txt > merged.txt   # 本职工作:拼接两个文件

less分页阅读大文件

命名
一个程序员冷笑话。早期的分页工具叫 more("再来一页"),后来的改进版反其道命名为 less,取谚语 "less is more"(少即是多)。
bash
less app.log
# 操作:空格翻页,/关键词 搜索,n 下一个匹配,q 退出

head / tail只看开头 / 结尾

命名
直白的"头"和"尾"。
bash
head -n 20 file.log     # 前 20 行
tail -n 50 file.log     # 后 50 行
tail -f app.log         # -f = follow:跟住文件,实时打印新增内容(盯日志神器)

wc统计数量

命名
word count,数单词。但最常用的其实是数行数。
bash
wc -l file.swift        # -l = lines:统计行数

grep在内容里搜索

命名
本文最值得讲的词源。上古编辑器 ed 中,"对全文(global)应用正则表达式(regular expression)并打印(print)匹配行"的指令写作 g/re/p。用的人太多,干脆独立成命令。所以 grep 天生就是"全局正则搜索打印"。

顺带认识正则表达式:一门用符号描述"什么样的文本算匹配"的迷你语言——^ 表示行首、$ 行尾、. 任意一个字符、* 重复任意次。新手完全可以先把 grep 当普通文本搜索用,等哪天需要"匹配所有以 test 开头的行"时再回头学正则,它值得。

bash
grep "TODO" main.swift          # 在文件里找
grep -rn "TODO" Sources/        # -r 递归整个目录,-n 显示行号(最常用组合)
grep -i "error" app.log         # -i = ignore case:忽略大小写
grep -v "debug" app.log         # -v = invert:反选,排除含 debug 的行
grep -c "func" main.swift       # -c = count:匹配的行数(一行里出现多次只算一行)

find按名字/属性找文件

grep 搜"内容",find 搜"文件本身"(名字、大小、修改时间)。

bash
find . -name "*.swift"                 # 从当前目录递归找 Swift 文件
find . -name "*.png" -size +1M         # 大于 1MB 的图片
find . -name ".DS_Store" -delete       # 找到并删除(清理 Finder 的缓存文件)

管道与重定向:Unix 的灵魂

Unix 哲学:每个程序只做好一件事,复杂任务靠组合。这句话出自管道的发明者 Doug McIlroy,他的原版表述值得一读:

写只做一件事、并把它做好的程序;写能协同工作的程序;写处理文本流的程序——因为文本流是通用接口。

前面学的命令确实都很"小":ls 只会列、grep 只会搜、wc 只会数。但三十个小命令一旦可以自由拼接,能表达的操作是无穷的——这就是命令行诞生六十年仍不可替代的原因。组合的语法只有三个符号:

bash
命令A | 命令B     # 管道:A 的输出作为 B 的输入
命令 > 文件       # 重定向:输出写入文件(覆盖)
命令 >> 文件      # 追加写入
命名
| 形如一根管子(pipe),数据像水一样从左流到右。

实际体验一下组合的威力:

bash
ls -la | grep "\.swift"                # 列目录,只看 Swift 文件(\. 把正则里的"任意字符"转义回句点本身)
grep -r "TODO" Sources/ | wc -l        # 全项目有多少行带着 TODO
history | grep "git"                   # 我之前敲过哪些 git 命令?
xcodebuild ... > build.log 2>&1        # 构建日志存档(2>&1 表示错误输出也一并收进来)

几个高频小工具

bash
echo "Hello"            # 回声:原样打印
which swift             # "哪一个":这个命令的可执行文件在哪个路径
history                 # 历史命令列表
命名
echo 即"回声",喊什么回什么。which 即"是哪一个",回答"我敲 swift 时系统实际运行的是哪个文件"。

which 引出一个值得现在就懂的概念——PATH

你有没有想过,敲 ls 时 Shell 怎么知道去哪找这个程序?答案是一个叫 PATH 的环境变量(environment variable,Shell 的全局设置项)。它的值是一串用 : 分隔的目录清单,Shell 按顺序在这些目录里找同名的可执行文件,找到即运行。

bash
echo $PATH        # 打印你的清单($ 前缀的意思是"取这个变量的值")
# /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:...

由此可以解释两个新手常见现象:报错 command not found = "清单上的目录翻遍了,没这个名字"(多半是没安装,或装到了清单之外的位置);而 Homebrew 装完某些工具让你改 ~/.zshrc,往往就是在把它的目录加进 PATH。


4. 系统管理:进程、磁盘与权限

先补一个基础概念。程序(program)是躺在磁盘上的一堆文件;进程(process)是程序被运行起来后、活在内存里的实例——一个在动的副本。打开两个 Safari 窗口跑着的是同一个程序、可能是同一个进程;而每敲一次 ls 都会诞生一个短命的新进程,干完活就消失。每个进程出生时领到一个编号,叫 PIDprocess identifier)。

你早就见过进程管理器——"活动监视器"就是。本章的 ps 和 top 是它的命令行形态。

ps / top看进程

命名
ps = process status,进程状态。top 即"最顶端"——按资源占用排序,最吃 CPU 的排最上面。
bash
ps aux                      # 列出所有进程(a=含其他用户的 u=详细格式 x=连无终端的后台进程也算上;合起来≈全部)
ps aux | grep Simulator     # 配合管道找特定进程
top -o cpu                  # 实时监控,按 CPU 排序,q 退出

kill终止进程

命名
名字吓人,本质温和——它的原义是给进程"发送信号"(signal),默认信号恰好是"请退出"。-9 是第 9 号信号 SIGKILL:不再商量,立即处决。
bash
kill 12345          # 礼貌地请 PID 为 12345 的进程退出
kill -9 12345       # 强制终止(进程无响应时)
killall Simulator   # 按名字终止(kill all matching)

sudo借用管理员权限

命名
superuser do,"以超级用户的身份去做"。超级用户是 Unix 里权限不设限的账号,名叫 root(根)——它拥有根目录 /,站在整棵权限之树的根上,一切在其之下。
bash
sudo xcode-select -s /Applications/Xcode.app
# 会要求输入你的登录密码;之后短时间内不再重复询问

用前默念安全守则第 2 条:系统不拦 root,敲错自己兜底。

chmod / chown权限与归属

命名
change mode(改权限模式)、change owner(改所有者)。ch- 前缀家族。
bash
chmod +x deploy.sh     # 给脚本加"可执行"权限(写完脚本跑不动,八成是缺这个)
ls -l                  # 开头的 rwxr-xr-x 就是权限:r 读 w 写 x 执行

df / du磁盘还剩多少 / 谁占的

命名
disk free(磁盘剩余)与 disk usage(磁盘用量)。一对镜像命令:df 看"还有多少",du 看"用了多少"。
bash
df -h                          # 各分区剩余空间(-h 又是 human-readable)
du -sh *                       # 当前目录每一项占多大(-s = summary 只给总数)
du -sh * | sort -rh | head -5  # 组合技:找出最大的 5 个
du -sh ~/Library/Developer/*   # 开发者的磁盘杀手基本都住这里

命名家族:-ctl、-util 和 -d

macOS/Unix 的系统命令有三个高频后缀,认识它们,看到新命令就能猜出身份:

后缀含义例子
-ctlcontrol,某服务的控制器launchctl(控制 launchd)、simctl(控制模拟器)
-utilutility,某类东西的工具箱diskutil(磁盘)、plutil(plist)、textutil(文档)
-ddaemon,常驻后台的守护进程launchdsshd——它们不是给你敲的,是背后默默运行的
daemon(守护神)是 Unix 对后台服务的老式浪漫称呼。launchctl 读作 "launch control":控制 macOS 的服务总管 launchd。
bash
diskutil list                          # 磁盘与分区
launchctl list | grep com.apple       # 当前加载的后台服务
uname -m                               # unix name:查架构(arm64 / x86_64)
sw_vers                                # software version:macOS 版本号

5. macOS 特色命令:苹果的命名品味

这一章的命令都是 macOS 独有的,Linux 上没有。苹果的命名比传统 Unix 命令更完整、更具象,反而好记。

pbcopy / pbpaste剪贴板管道

命名
pasteboard = 苹果对剪贴板的正式称呼(写 Apple 平台 app 的话,对应的 API 正是 NSPasteboard / UIPasteboard——命令行和框架同源)。
bash
cat ~/.ssh/id_ed25519.pub | pbcopy    # 公钥复制进剪贴板,去网页上粘贴
pbpaste > snippet.swift               # 剪贴板内容存成文件
pbpaste | wc -l                       # 剪贴板里有几行?

mdfind命令行版 Spotlight

命名
metadata find,按元数据查找。Spotlight 的底层是一套元数据索引:find 现场遍历目录,mdfind 查现成的索引,所以通常快得多。代价是依赖索引——被排除在 Spotlight 之外的目录、刚写入还没被索引的文件,它就查不到;那种时候还得请 find 出马。
bash
mdfind -name "Package.swift"                  # 全盘按名字找(走索引,一般秒出)
mdfind -onlyin ~/Projects "URLSession"        # 限定范围;对已索引的常见文件类型,还能搜到文件内容

defaults读写偏好设置

命名
来自 macOS 的"用户默认设置数据库"(user defaults database)。你在 app 里用的 UserDefaults API,读写的就是同一套东西——命令行是它的直接入口。
bash
defaults read com.yourname.MyApp        # 看你 app 存了什么(调试 UserDefaults 直观)
defaults delete com.yourname.MyApp      # 清空,模拟"首次启动"
defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder
                                        # 让 Finder 显示隐藏文件

caffeinate不许睡觉

命名
本章最佳命名。caffeinate = "给……灌咖啡因"。给 Mac 灌咖啡,它就不睡了。
bash
caffeinate -i ./long_task.sh    # 任务执行期间阻止睡眠,结束自动解除
caffeinate -d -t 3600           # display:保持屏幕常亮 1 小时

sips命令行图像处理

命名
scriptable image processing system,可脚本化的图像处理系统。
bash
sips -z 512 512 icon.png --out icon-512.png     # 缩放(-z 后先高再宽)
sips -s format jpeg in.png --out out.jpg        # 转格式
sips -g pixelWidth -g pixelHeight image.png     # -g = get:查属性

写个循环就能批量生成全套 App Icon 尺寸,不必开任何软件。

其他好玩且有用的

bash
say "构建完成"               # 朗读文字。挂在长命令后当提醒:xcodebuild ... && say 完成
screencapture -i shot.png    # 截图(-i = interactive 交互选区)
ditto -c -k --keepParent MyApp.app MyApp.zip
                             # 复制/打包并完整保留 macOS 元数据,公证前打包用它
networkQuality               # 苹果自带测速(macOS 12+)
qlmanage -p file.pdf         # 命令行触发 Quick Look(ql = Quick Look)
命名
ditto 是拉丁语血统的英文词,意为"同上、原样照抄"——比 cp 更忠实的复制。say 就是"说"。

6. Homebrew:Mac 的软件"酿酒厂"

什么是包管理器(package manager):命令行世界的 App Store——统一负责软件的安装、升级、卸载。但它比 App Store 多干一件关键的事:处理依赖(dependency)。命令行软件常常建立在别的软件之上(A 依赖 B,B 又依赖 C),包管理器知道整张依赖网,一条命令连锁装齐。这是个通用概念,你会反复遇到它的各路亲戚:Linux 的 apt、JavaScript 的 npm、Swift 的 SPM——思想全部相通。

macOS 没有自带的命令行包管理器,Homebrew 是社区的事实标准。第 10 章的所有工具都靠它安装。

命名
Homebrew = "家酿啤酒"。作者把"在自己 Mac 上编译软件"比作自家酿酒,并沿着这个比喻构建了一整套术语宇宙——理解比喻,所有概念一次记牢:
术语字面义实际含义
formula配方一个命令行软件的安装脚本
cask酒桶图形界面 app("大桶装")
bottle瓶装酒预编译好的包(不用现场"酿",开瓶即饮)
tap酒桶龙头接入第三方软件源(接上龙头,酒就流过来)
cellar酒窖软件的实际安装目录

安装 Homebrew 本身(官网 brew.sh 的一行命令),然后:

bash
brew install ripgrep                  # 装命令行工具(formula)
brew install --cask rectangle        # 装图形 app(cask)
brew search json                      # 搜索有什么可装
brew info node                        # 查看包详情
brew list                             # 已安装清单
brew upgrade                          # 全部升级
brew uninstall node                   # 卸载
brew cleanup                          # 清理旧版本,释放磁盘
brew doctor                           # "看医生":体检环境问题

进阶两招:

bash
# 环境迁移:当前机器所有 brew 软件导出成清单,新 Mac 一键装回
brew bundle dump          # 生成 Brewfile
brew bundle               # 按 Brewfile 全部安装

# 后台服务管理(本地数据库等)
brew services start postgresql
brew services list

7. Git:给代码拍快照

先说它解决什么问题

没有版本控制的世界你多半见过:报告-最终版.docx报告-最终版2.docx报告-真的最终版.docx版本控制(version control)是这个困境的系统化解法:它把项目每次有意义的变化记录成一张快照(Git 里叫 commit),你可以随时查看任何一张、比较两张的差异、退回任何一张,还能和别人并行修改后再合并。写代码尤其需要它——改坏了能回头,多人协作不互相覆盖。

Git 诞生于 2005 年:Linus Torvalds 需要管理全球数千人协作的 Linux 内核源码,现有工具都不满意,于是自己动手,两周写出雏形。今天它是这个领域的绝对标准。

名字的来历

命名
git 是英式俚语,大意"讨厌鬼"。作者 Linus Torvalds 的自嘲:"我是个自大的混蛋,我把所有项目都用自己命名——先是 Linux,现在是 git。" 官方手册的自我介绍则是 "the stupid content tracker"(傻瓜内容追踪器)。

先建立心智模型,再背命令

Git 的所有命令都在这四个区域之间搬运东西。看懂这张图,后面每个命令都有了位置感:

text
工作区            暂存区              本地仓库            远程仓库
(working dir)    (staging area)     (repository)       (origin)
你正在改的文件  →  打包待提交的改动  →   已保存的快照历史  →   GitHub 上的副本
           git add            git commit           git push
           ←──────────────── git restore          ←─── git pull / fetch
  • 暂存区(staging area):一个"待提交清单"。改了 10 个文件,可以只挑 3 个放进清单,作为一次干净的提交。
  • commit:一次快照。词义双关——既是"提交",也是"承诺":提交了就写进历史。
  • origin:不是什么魔法词,只是"你 clone 来源的那个远程仓库"的默认代号。

7.1 初始配置(一次性)

bash
git config --global user.name "Jerry"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
config --global = 全局配置,写入 ~/.gitconfig,所有仓库共享。

7.2 日常循环:五个命令过一天

命名
clone 克隆(完整复制远程仓库);status 状态;add 添加进暂存区;push/pull 推/拉——方向以你的电脑为基准:push 推出去,pull 拉回来。
bash
git clone https://github.com/user/repo.git    # 第一次:克隆
git status -sb            # 现在什么状态?(-s 简洁 -b 显示分支)
git add -p                # 交互式暂存:逐块确认改动(比 add . 更能做出干净提交)
git commit -m "feat: 支持深色模式"
git push                  # 推到远程
git pull                  # 拉取别人的更新
fetch vs pullfetch(取回)只下载远程的新提交、不动你的代码;pull = fetch + 整合——默认用 merge 整合,也可以配置成 rebase(git config pull.rebase true)。想先看看再决定,用 fetch。

7.3 查看历史:发生过什么

命名
log 即日志(源自航海日志 logbook);diff = difference,差异;show 展示;blame 直译"责怪"——"这行代码是谁写的?!"名字促狭,用途正经:逐行标注最后修改者。
bash
git log --oneline --graph -10     # 最近 10 条历史:一行一条 + 分支拓扑图
git log -p file.swift             # 某个文件的历次改动细节(-p = patch 补丁)
git diff                          # 我改了什么(工作区 vs 暂存区)
git diff --staged                 # 即将提交什么(暂存区 vs 上次提交)
git show abc123                   # 查看某次提交的完整改动
git blame -L 10,20 file.swift     # 第 10–20 行分别出自谁的哪次提交

建议养成的习惯:每次 commit 前先 git diff --staged 过一眼——确认要提交的正是你以为的东西,很多事故止于这一眼。

7.4 分支:平行世界

命名
branch 即"树枝"——从主干上分杈,长自己的提交,成熟后 merge(合流)回主干。HEAD 是"你现在站的位置":当前检出的那个提交。
bash
git switch -c feature/dark-mode    # -c = create:创建并切换(switch 是现代命令,语义清晰)
git switch main                    # 切回主干
git branch                         # 列出分支,* 标记当前位置
git merge feature/dark-mode        # 把功能分支合并进当前分支
git branch -d feature/dark-mode    # 删除已合并的分支(-d = delete)

7.5 后悔药:每个"撤销"命令的准确含义

Git 的撤销命令多,因为"后悔"分很多种。按词义区分,不会用错:

命名
restore 恢复原状;reset 重置(把历史指针往回拨);revert 反转(用一次新提交抵消旧提交);amend 修补(英语里"修正案"的那个词);stash 藏匿处(把半成品藏起来);reflog = reference log,引用日志——HEAD 每次移动的黑匣子。
bash
git restore file.swift             # 后悔改了:丢弃工作区的修改
git restore --staged file.swift    # 后悔 add 了:从暂存区撤下
git commit --amend                 # 后悔刚才的提交:修补信息或补充漏掉的文件
git reset --soft HEAD~1            # 撤销最近一次提交,改动保留(soft 温柔)
git reset --hard HEAD~1            # 连提交带改动全部抹掉(hard 无情,慎用)
git revert abc123                  # 已推送的提交要撤销时用它:不改历史,追加反向提交
git stash                          # 改到一半要切分支:先藏起来
git stash pop                      # 回来后取出(pop:像从栈顶弹出)
git reflog                         # 黑匣子:reset 错了、分支删错了,来这里找回坐标
git reset --hard HEAD@{2}          # 按黑匣子记录回到误操作之前

记住 reflog 的存在——提交过的东西大多能从这里找回。但别把它当备份:它只存在于你本机,且记录会定期过期清理(默认保留约 90 天、不可达的约 30 天)。真正的保险,仍然是及时推送到远程。

7.6 进阶四件套

命名
rebase = re-base,"重新设定基底"——把你的分支挪到新的起点上重演,历史成一条直线;cherry-pick 摘樱桃——只摘别的分支上想要的那一颗提交;bisect = bi(二) + sect(切),二分查找;worktree 工作树——同一仓库长出多个工作目录。
bash
git rebase main               # 把当前分支重新"嫁接"到 main 最新处
git rebase -i HEAD~3          # -i 交互式:合并、改写、丢弃最近 3 个提交(推送前整理历史)

git cherry-pick abc123        # 摘取某个提交到当前分支

git bisect start              # 二分定位是哪个提交引入了 bug:
git bisect bad                #   标记当前版本有问题
git bisect good v1.2.0        #   标记这个老版本没问题
                              #   之后 git 自动跳转,每测一次标记一次,log₂n 次锁定凶手
git bisect reset              # 结束,回到原分支

git worktree add -b hotfix/crash ../hotfix   # -b 新建分支并检出到新目录,同时干两件事不用 stash
                                             # (分支已存在时去掉 -b,直接写分支名)

7.7 .gitignore

告诉 Git 哪些文件永远不追踪。Apple + Web 项目常用:

gitignore
.DS_Store
xcuserdata/
DerivedData/
.build/
Pods/
node_modules/
.env

8. 网络命令

先铺三个基础概念,本章命令都建立在它们之上:

  • IP 地址:设备在网络中的门牌号,形如 17.253.144.10
  • 域名与 DNS:人记不住数字门牌,于是有了 apple.com 这样的域名;DNS(domain name system)就是那本把域名翻译成 IP 的全球电话簿。
  • 端口(port):同一个 IP 上不同服务的舱门编号。网页走 80(HTTP)和 443(HTTPS),你本地起的开发服务器常在 3000 或 8080。"端口被占用"=那扇门已有进程守着。

ping对方活着吗

命名
拟声词,来自潜艇声呐的"乒"声——发出脉冲,听回声判断目标是否存在、距离多远。网络 ping 原理一模一样:发个小包,等回音,报告往返耗时。
bash
ping -c 4 apple.com     # -c = count:只发 4 个包(不加会一直 ping,Ctrl-C 停)
# 回显里能看到解析出的 IP 和每包往返毫秒数。ping 得通说明网络可达;
# ping 不通则别急着定罪——不少服务器和防火墙会直接屏蔽 ping

curl命令行浏览器

命名
client for URLs,"URL 的客户端"。任何能用 URL 表达的请求它都能发,是开发者调 API 的第一工具。
bash
curl https://api.github.com/zen          # 最简单:GET 并打印
curl -s https://api.example.com/users | jq    # -s = silent:安静模式,配合 jq 格式化 JSON
curl -LO https://example.com/file.zip    # 下载:-L 跟随重定向,-O 按原名保存
curl -sI https://example.com             # -I = head:只看响应头
curl -v https://api.example.com          # -v = verbose:显示完整请求过程,调试用

# 发 POST 请求(写 app 前先验证接口行为)
curl -X POST "https://api.example.com/users" \
  -H "Content-Type: application/json" \
  -d '{"name": "Jerry"}'
选项也讲命名:-X 指定方法,-H = Header,-d = data。

ssh / scp安全地远程登录与传文件

命名
secure shell(加密的远程 Shell)与 secure copy(加密版 cp——注意它的参数顺序和 cp 完全一致:从哪里,到哪里)。
bash
ssh [email protected]                          # 登录远程服务器
ssh-keygen -t ed25519 -C "[email protected]"   # key generator:生成密钥对
ssh-copy-id [email protected]                  # 把公钥装到服务器上,之后免密登录
scp file.zip [email protected]:/var/www/       # 上传
scp [email protected]:/var/log/app.log .       # 下载("到哪里"是当前目录 .)

密钥对(key pair)值得一个比喻:ssh-keygen 生成一把(公钥,public key,可以随便给人)和一把钥匙(私钥,private key,永远不离开你的 Mac)。ssh-copy-id 是把锁挂到服务器门上;之后持有配对私钥的你推门即入。GitHub 免密推送用的也是同一套机制——把公钥贴到 GitHub 设置里,就是"挂锁"。

lsof谁占用了这个端口

命名
list open files,列出打开的文件。为什么查端口的命令叫"列文件"?因为 Unix 哲学:一切皆文件——网络连接在系统眼里也是一种文件。
bash
lsof -i :3000               # 3000 端口被谁占了(Web 开发日常)
kill -9 $(lsof -ti :3000)   # 组合技:直接杀掉占用者(-t 只输出 PID,正好喂给 kill)

dig查域名解析

命名
domain information groper,"域名信息摸索器",也正好是英文"挖掘"——挖 DNS 记录。
bash
dig apple.com +short         # 域名解析到哪个 IP(+short 只给答案)
dig @8.8.8.8 apple.com       # 指定用 Google 的 DNS 服务器查(对比排查解析问题)

改了 DNS 不生效时,清一下缓存:

bash
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

9. Apple 开发工具链

先弄清本章的核心动词——构建(build):把人写的源代码变成机器能运行的 app 的全过程,包括编译(compile,把代码翻译成机器指令)、链接(link,把各模块与系统库拼装成一体)、打包资源(图标、素材等)。在 Xcode 里按 ⌘B 就是构建;本章命令是同一件事的命令行形态。为什么需要命令行形态?因为自动化——CI 服务器(每次提交代码后自动打包测试的机器)上没有人替你按 ⌘B。

这一章命令偏长,但命名都很直白——苹果的现代工具几乎不搞缩写。

xcode-select选择用哪个 Xcode

命名
字面义"Xcode 选择器"。装了正式版 + beta 版时,命令行工具听谁的,由它决定。
bash
xcode-select --install       # 新 Mac 第一步:安装命令行开发工具(git 等都靠它)
xcode-select -p              # -p = print path:当前生效的 Xcode 路径
sudo xcode-select -s /Applications/Xcode-beta.app    # -s = switch:切换

xcrun工具链的大门

命名
Xcode run,"通过 Xcode 运行"。Xcode 工具链里藏着上百个小工具(simctl、notarytool……),它们不在系统 PATH 里,统一经 xcrun 调用。看到 xcrun xxx,读作"运行 Xcode 里的 xxx"。
bash
xcrun --find swift               # 找出工具的实际路径
xcrun --show-sdk-path            # 当前 SDK 在哪

xcodebuild命令行构建

命名
Xcode + build,无需解释。它就是 Xcode 按下 ⌘B 的命令行形态。
bash
xcodebuild -list                                  # 列出项目的 scheme
xcodebuild -scheme MyApp build                    # 构建
xcodebuild test -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16'    # 在模拟器上跑测试
xcodebuild archive -scheme MyApp -archivePath ./build/MyApp.xcarchive   # 打包

输出很吵,建议配第 10 章的 xcbeautify 美化。

simctl模拟器遥控器

命名
simulator control——第 4 章的 -ctl 家族成员。
bash
xcrun simctl list devices | grep Booted        # 哪台模拟器开着
xcrun simctl boot "iPhone 16 Pro"              # 开机
xcrun simctl openurl booted "myapp://detail?id=42"   # 测试深链(booted = 当前开机的那台)
xcrun simctl push booted com.me.MyApp push.apns      # 发模拟推送
xcrun simctl io booted screenshot shot.png     # 截图
xcrun simctl status_bar booted override --time "9:41" --batteryLevel 100
                                               # 商店截图专用:满电 + 9:41——初代 iPhone
                                               # 发布会亮相定格的时刻,苹果宣传图的传统
xcrun simctl delete unavailable                # 删掉失效的旧模拟器,常能省几十 GB
xcrun simctl erase all                         # 全部恢复出厂

codesign / security签名排查

命名
code + sign,给代码签名。security 管钥匙串——证书都住在里面。
bash
codesign -dv --verbose=4 MyApp.app             # 这个 app 用什么证书签的
security find-identity -v -p codesigning       # 我机器上有哪些可用的签名身份

报"没有证书"类错误时,先用第二条确认证书是否真的在钥匙串里。

notarytool + stapler公证流水线

命名
notarize = 公证。用 Developer ID 证书签名、在 App Store 之外分发的 macOS app,需要先把包交给苹果"公证处"做自动安全扫描,否则用户打开时会被 Gatekeeper 拦下。stapler = 订书机——公证通过后把票据(ticket)"钉"在 app 上;不钉票据 Gatekeeper 也能联网查验,钉上的价值是离线也能通过验证。一个流程,两个具象的名字。
bash
xcrun notarytool submit MyApp.zip \
  --apple-id [email protected] --team-id TEAMID \
  --password "app专用密码" --wait               # 提交并等待结果
xcrun stapler staple MyApp.app                  # 通过后钉上票据

plutilplist 工具箱

命名
property list utility,-util 家族成员。
bash
plutil -p Info.plist                            # 友好格式打印
plutil -lint Info.plist                         # lint:语法体检
plutil -replace CFBundleShortVersionString -string "2.0" Info.plist   # 脚本改版本号

崩溃排查二人组

命名
atos = address to symbol,把崩溃日志里的十六进制地址翻译回函数名。dwarfdump 导出 DWARF 调试信息——这个名字是冲着 ELF(精灵,一种 Unix 可执行文件格式)起的奇幻文字梗:有精灵,就得有矮人。如今 DWARF 是跨平台的调试信息标准,macOS 的 Mach-O 可执行格式同样用它。
bash
dwarfdump --uuid MyApp.app.dSYM     # 确认 dSYM 和崩溃日志的 UUID 是否匹配
atos -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \
  -arch arm64 -l 0x100000000 0x1000a4c2c        # 地址 → 函数名+行号

swiftSwift 工具链与包管理

bash
swift repl                     # 交互式环境,随手验证语法
swift run                      # 运行 SPM 可执行项目
swift build && swift test      # 构建 + 测试
swift package init             # 新建一个 Swift Package
swift package update           # 更新依赖

日常维护

bash
rm -rf ~/Library/Developer/Xcode/DerivedData   # Xcode 行为诡异时的万能药(缓存目录,可放心删)

10. 现代效率工具:命名彩蛋大全

以下全部 brew install <名字> 安装。它们是传统命令的现代替代品,更快、默认值更聪明,且名字一个比一个有梗。

ripgrep (rg)更快的 grep

命名
rip 有"撕开、飞速掠过"之意——把文件堆撕开来搜。命令名缩成两个字母 rg,向 Unix 传统致敬。
bash
rg "TODO"                     # 递归搜索,自动跳过 .gitignore 里的内容(node_modules 等)
rg -t swift "URLSession"      # -t = type:只搜某类文件
rg -l "deprecated"            # 只列文件名

fd更快的 find

命名
find 砍掉多余字母就是 fd——名字本身就是它的产品宣言:更短、更简单。
bash
fd '\.swift$'                 # 找 Swift 文件(对比 find . -name "*.swift")
fd -e png                     # -e = extension:按扩展名
fd -H '\.DS_Store' -x rm      # -H 含隐藏文件,-x = execute:找到即执行

fzf万能模糊搜索

命名
fuzzy finder,模糊查找器。"模糊"意为不必精确输入,敲几个字母就能命中。
bash
git branch | fzf | xargs git switch    # 模糊选分支并切换

再启用它的 Shell 集成——在 ~/.zshrc 里加一行 source <(fzf --zsh)——之后 Ctrl-R 模糊搜命令历史、Ctrl-T 模糊选文件路径。单论提升终端效率,此工具性价比第一。

xargs = 把上一步的输出变成下一个命令的参数(arguments),管道世界的转接头。

bat带高亮的 cat

命名
双关。既是 cat(猫)→ bat(蝙蝠,"长了翅膀的猫"),又暗示 better cat。
bash
bat main.swift                # 语法高亮 + 行号 + Git 修改标记

其余快速过一遍

工具替代谁命名来源一句话
ezalsexa 的社区继任者eza -la --git 显示 Git 状态的 ls
jqJSON queryJSON 的瑞士军刀:`curl -s url \
tldrmantoo long; didn't read只给例子的手册,新手第一查
ghGitHub 官方 CLIgh pr create 不离终端发 PR
htoptoph = 作者 Hisham可交互、可视化的 top
treels -Rtree -L 2 打印目录树
hyperfine超精细基准测试:hyperfine "cmd1" "cmd2" 对比谁快
xcbeautifyXcode + beautify`xcodebuild ... \

附录 A:命名规律速查表

缩写型(展开即记住)

命令全称
pwdprint working directory我在哪
lslist列出
cdchange directory切换目录
cp / mv / rmcopy / move / remove复制 / 移动 / 删除
lnlink链接
wcword count计数
psprocess status进程状态
df / dudisk free / disk usage磁盘剩余 / 用量
manmanual手册
chmod / chownchange mode / owner改权限 / 改所有者
unameunix name系统信息

合成与首字母型

命令来源
mkdirmake directory
greped 编辑器指令 g/re/p(global / regex / print)
sedstream editor(流式编辑器)
awk三位作者姓氏首字母:Aho、Weinberger、Kernighan
tartape archive(磁带归档——名字暴露年龄)
sudosuperuser do
ssh / scpsecure shell / secure copy
curlclient for URLs
lsoflist open files
digdomain information groper
fzffuzzy finder
tldrtoo long; didn't read

比喻型(有画面就忘不掉)

命令画面
brew 全家酿酒厂:formula 配方、cask 酒桶、tap 龙头、cellar 酒窖、bottle 瓶装
git stash藏匿处:半成品先藏起来
git cherry-pick摘樱桃:只摘想要的那颗提交
git rebase重设基底:把树枝嫁接到新位置
caffeinate灌咖啡因:Mac 不许睡
stapler订书机:把公证票据钉在 app 上
ping声呐"乒"一声
less"less is more":more 的升级版
bat长翅膀的猫(better cat)
daemon (-d 后缀)守护神:后台默默运行的服务

通用选项与后缀

text
选项:-a all │ -l long │ -h human/help │ -R recursive │ -f force
      -v verbose │ -i interactive │ -c count │ -n number │ -p print/parents
后缀:-ctl = control(控制器) │ -util = utility(工具箱) │ -d = daemon(后台服务)

再次强调这些是惯例而非标准,例外常见:ls 的 -r 是 reverse 反序(递归要用 -R)、grep 的 -c 数的是行数而非次数。拿不准,man 说了算。


附录 B:高频组合技

学完管道之后,这些一行流会自然看懂:

bash
# 杀掉占用 3000 端口的进程
kill -9 $(lsof -ti :3000)

# 找出当前目录最大的 10 个文件/文件夹
du -sh * | sort -rh | head -10

# 统计项目 Swift 代码行数(排除依赖目录)
fd -e swift -E Pods -E .build | xargs wc -l | tail -1

# 构建完成语音提醒
xcodebuild -scheme MyApp build | xcbeautify && say "构建完成"

# 把最近一次提交改动的文件列表复制到剪贴板
git diff --name-only HEAD~1 HEAD | pbcopy   # 明确比较两个提交,避免混入未提交的改动

# 全项目有多少行带着 TODO
rg "TODO" | wc -l

# 清理开发磁盘三连(先看大小,再动手)
du -sh ~/Library/Developer/Xcode/DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData
xcrun simctl delete unavailable

附录 C:学习路径建议

不要试图一次记住全部。按周推进,每天 10 分钟真实使用胜过一次通读:

  • 第 1 周:第 0–2 章。只练 pwd / ls / cd / mkdir / cp / mv / rm / open,强迫自己用终端代替 Finder 做文件操作。
  • 第 2 周:第 3 章。重点吃透 grep 和管道 |,开始用 history | grep xxx 找自己敲过的命令。
  • 第 3 周:第 6–7 章。装 Homebrew,把 Git 日常五命令(status / add / commit / push / pull)变成肌肉记忆,理解四区域图。
  • 第 4 周:第 4、5、8 章按需查用;装上第 10 章的 fzf / rg / tldr——此后遇到新命令,先 tldr 一下。
  • 之后:第 9 章在真实的打包、签名、公证场景中边用边学;Git 进阶命令(rebase / bisect / reflog)等需要时回来查。

记忆的关键不是背,而是每次敲命令前默念一遍它的全称:print working directory、change directory、superuser do……两周后,缩写会自己长在手上。