外观
替换常用命令行工具:收益、代价与退路
macOS 自带的 ls、grep、find 来自 BSD,选项和 Linux 上常见的 GNU 版本不完全一样,输出也朴素。社区里有一批重写的替代品,输出更易读、默认行为更符合直觉。
这一篇的重点不是列清单,而是讲清楚替换的代价:你会逐渐失去在别人的机器、服务器和救援环境里干活的基本功。先想清楚这一点,再决定装哪些。
开始之前
需要先装好 Homebrew,见使用 Homebrew 管理工具;需要知道自己的配置写在哪个文件里,见zsh 启动文件与加载顺序。
本文所有工具都可以用 brew uninstall 卸载,卸载后系统自带命令立即恢复工作——前提是你没有用别名覆盖它们,原因见下。
五个值得考虑的替换
| 替换谁 | 工具 | 主要收益 |
|---|---|---|
ls | eza | 彩色分类、树状视图、显示 Git 状态 |
cat | bat | 语法高亮、行号、自动分页 |
grep | ripgrep(命令名 rg) | 默认递归、自动跳过 .gitignore 里的文件、速度快 |
find | fd | 语法简单得多,默认忽略隐藏文件和版本库目录 |
| —— | fzf | 交互式模糊筛选,可以接在任何命令的输出后面 |
装其中一个试试:
sh
brew install ripgrepbrew install 从 Homebrew 安装软件包,ripgrep 是包名。装完后命令是 rg 而不是 ripgrep——包名和命令名不一致是常见情况,brew info ripgrep 可以看到说明。
在一个项目目录里对比一次:
sh
grep -rn "TODO" .
rg "TODO"grep 的 -r 表示递归子目录,-n 表示显示行号,. 是搜索起点。rg 默认就递归、显示行号,并且跳过 .gitignore 列出的文件——这既是它快的原因,也是它可能漏掉你想找的文件的原因。需要包含被忽略的文件时加 -u。
别用别名覆盖原命令
最常见的建议是在配置文件里写 alias ls='eza'、alias cat='bat'。不要这样做,理由有三个:
- 参数不兼容。 你习惯的
ls -lh在eza下可能表现不同;别人给你的命令粘进来会报错,而你已经忘了ls现在不是ls。 - 脚本会出问题。 别名在交互式 shell 里生效,脚本中不生效,于是同一条命令在两处行为不同——这类问题排查起来非常费时。
- 换机器就失灵。 服务器、同事的电脑、恢复模式里的终端都没有这些工具。肌肉记忆建立在别名上,到了那些环境你会卡住。
更稳妥的做法是保留原命令,只给替代品加短别名:
sh
alias ll='eza -l --git'-l 是长格式,--git 让它显示每个文件的 Git 状态。ls 仍然是 ls,你随时可以用。
确实想让 cat 带高亮时,用一个新名字而不是覆盖:alias c='bat'。这样你既得到便利,又保留了原来的行为。
fzf 是唯一值得优先装的
前面四个是「更好的同类」,fzf 提供的是原本没有的能力:把任何命令的输出变成可以模糊筛选的列表。
sh
brew install fzf装完后在终端按 Control + R,历史命令会变成可搜索的列表;输入片段即可筛选,回车执行。这个功能在没有 fzf 的机器上不存在,所以它是净增量,而不是替换。
需要按下 Control + R 没有反应时,说明 shell 集成还没加载。fzf 的 Homebrew 安装说明里会给出要写进配置文件的那一行,用 brew info fzf 查看。
检查结果
打开一个新的终端窗口,确认三件事:ls、grep、find 仍然是系统自带的行为;你新加的别名能用;Control + R 能调出历史搜索。
用 type ls 检查一个命令到底是什么:输出 ls is /bin/ls 表示还是原命令,输出 ls is an alias for ... 说明被覆盖了。type 是 shell 内置命令,用来查一个名字最终会执行什么。
动手练习
在一个练习目录里完成一组对照,不要改任何配置:
- 用
grep -rn和rg搜同一个词,比较输出和耗时。 - 建一个
.gitignore写入*.log,再放一个test.log进去,分别用两个工具搜索它的内容。 - 用
type grep确认你没有意外覆盖原命令。
成功标准是能说出第二步里 rg 为什么搜不到,以及加什么参数能搜到。理解「默认忽略什么」比记住工具名重要——这正是替换工具最容易踩的坑。