fish — 装好就比 bash 加插件好用的交互 shell
是什么
fish 全名 Friendly Interactive Shell,是一个给人用、不给脚本用的交互式命令行。日常类比:bash 像功能机的拨号键盘,fish 像智能手机——你打前两个字母,它把整句话灰色补出来,按右箭头就接受。
打开它,你看到的是这样:
$ git che_____________ ← 灰色:ckout main(来自历史)按右箭头,git checkout main 就填进去了。这种”打字时就能预览”的体验是 fish 默认就有,bash 要装一堆插件才接近。
为什么重要
不学 fish 也不影响你写代码,但理解它能让你看清两件事:
- shell 不是只能继承 1979 年的 sh —— fish 故意不兼容 POSIX,证明一个 shell 可以重新设计而不是抱着旧标准
- 默认值的力量 —— 同样是装好就用,bash 让你看
$和黑屏,fish 给你高亮、补全、Web 配置页,用户体验差出一个时代
如果你换新机器装 macOS / Linux,把默认 shell 从 zsh 切到 fish 大概是一行命令的小投资换长期回报。
核心要点
fish 默认开箱的四个能力,是其他 shell 要装插件才有的:
-
autosuggestions(灰色自动建议):从历史和补全里挑最可能的下一句,按 → 接受。你打字越多,它越懂你——它会按”最近用过、当前目录用过”加权排序。
-
syntax highlighting(语法高亮):命令存在变绿、不存在变红,路径存在变下划线。红色出现就是打错了,回车前就知道,省下一次报错往返。
-
从 man page 解析补全:装
git,fish 会读man git自动生成git的子命令补全。不需要维护一个补全文件——这是 fish 比 bash / zsh 长期领先的一招。 -
fish_config Web 配置页:在终端敲
fish_config,浏览器打开一个本地页,可视化改主题、看历史。没有第二个 shell 这么做。
加一个故意不兼容 POSIX的设计哲学:fish 不让你 source 旧的 .sh,因为它认为 POSIX 语法本身有坑(变量分词、引号嵌套、错误处理),与其修补不如重来。
实践案例
案例 1:变量赋值不一样
bash / zsh:
export PATH="$HOME/bin:$PATH"fish:
set -gx PATH $HOME/bin $PATH逐字解释:set 是赋值命令,-g 全局,-x 导出(export),多个值用空格分隔不用拼字符串——fish 的列表是真列表,不是空格分割的字符串。
案例 2:if / function 用 end 收尾
function greet set name $argv[1] if test -z "$name" echo "hi 陌生人" else echo "hi $name" endend对照 bash 用 fi 和 },fish 统一用 end——一个关键字关三种块(if / function / for),少记两个语法。
案例 3:universal variable(所有 fish 会话共享)
set -U FISHER_PLUGINS jorgebucaran/fisher-U 是 fish 独有的”universal”——写一次,所有打开的 fish 都看得到,关机重开也在。bash / zsh 没有等价物,必须写到 .bashrc 让每个新 shell 重读。
底层实现是 fish 把 universal 变量写到 ~/.config/fish/fish_variables,由后台守护进程通知所有运行中的 fish 实例实时更新。所以你在终端 A 改了 set -U EDITOR vim,终端 B 不用重开就生效。
案例 4:抽象函数(abbr)—— 比 alias 更聪明
abbr -a gco git checkout打 gco 然后空格,fish 当场把 gco 替换成 git checkout 显示出来——你看到完整命令、历史里也存完整命令。alias 是隐藏替换,abbr 是显式展开,调试和分享时不会混淆。
踩过的坑
-
服务器脚本不要写 fish:服务器默认没装 fish,CI 也大多用 bash。fish 只用于交互,写部署脚本仍用 bash / sh。
-
source旧 .sh 失败:很多工具的init脚本是 bash 语法(export FOO=bar),fish 直接报错。解决:用bass(fish 插件)翻译,或重写为.fish。 -
$(cmd)不工作:fish 用(cmd)做命令替换。心智切换成本最高的一个——肌肉记忆经常打错。 -
从 C++ 重写到 Rust:fish 4.0 用 Rust 重写。意味着旧版二进制和新版行为可能微妙不一致——升级前看一眼 changelog。
适用 vs 不适用场景
适用:
- 日常交互——cd / git / ls / vim 调用,几乎每个命令都受益
- 新机器零配置——装完
brew install fish就比配 bash 一周强 - 不写复杂 shell 脚本的桌面用户
不适用:
- 团队部署脚本(同事机器没装就跑不了)
- 需要 POSIX 兼容的 CI / 容器入口
- 重度 sh-script 生态(很多工具的
install.sh假设 bash)
历史小故事(可跳过)
- 2005 年:Axel Liljencrantz 在瑞典发布 fish 1.0,主打”对新手友好”,第一次把语法高亮带进 shell
- 2018 年:fish 3.0,确立现代功能闭环(autosuggestions / 高亮 / 补全 / Web 配置),周边插件管理器 Fisher 也在这时成熟
- 2024-2025 年:核心从 C++ 迁移到 Rust,4.0 启动;目的是降低维护门槛、吸引新贡献者,也借机把多年累积的内存/线程问题清理一遍
这条时间线说明 fish 不是热门一两年的工具,它是 20 年慢慢演化的”日常体验流派”代表。
学到什么
- shell 是工具不是教条——可以为了”日常好用”放弃 POSIX 兼容
- 默认值定义体验——同样 26k star 的项目,差别常常不在功能数而在”开箱第一分钟”
- “不兼容”也是一种选择——重写比修补更便宜的时候,敢于断舍离
延伸阅读
- 官方教程:fishshell.com/docs/current/tutorial.html(半小时上手)
- 对照表:fish-vs-bash cheat sheet
- Rust 重写动因:fish-shell/fish-shell #9512 讨论
- 插件入口:Fisher
关联
- warp —— warp 是 Rust 写的现代终端,与 fish 的”现代化交互”思路同向
- starship —— 跨 shell 通用 prompt,常和 fish 配对
- zsh —— fish 的另一个选择,POSIX 兼容但默认体验不如 fish
- neovim —— 同属”重写经典工具”哲学