Fish Shell: 开箱即用的现代 Shell¶
为什么要学 Fish Shell¶
Bash 和 Zsh 是强大的 Shell,但它们有一个共同的问题:需要大量配置才能好用。Zsh 用户通常需要 Oh My Zsh + 一堆插件才能得到语法高亮、自动补全、历史搜索等基础功能。
Fish(Friendly Interactive SHell)的设计哲学是:这些功能不应该是插件,而应该是默认行为。
| 功能 | Bash | Zsh (需要插件) | Fish (内置) |
|---|---|---|---|
| 语法高亮 | 无 | zsh-syntax-highlighting | 内置 |
| 自动建议 | 无 | zsh-autosuggestions | 内置 |
| Tab 补全 | 基础 | 需要配置 | 智能补全+描述 |
| 历史搜索 | Ctrl+R (基础) | fzf 插件 | 内置模糊搜索 |
| Web 配置界面 | 无 | 无 | fish_config |
| man 页面补全 | 无 | 需要配置 | 自动解析 man 页面 |
| 配置时间 | 2小时+ | 1小时+ | 0分钟 |
Fish 不追求 POSIX 兼容,而是选择了更现代、更直觉的语法设计。这是它最大的优势也是最大的争议点。
核心概念¶
白话解释¶
Fish 是一个交互式 Shell,它的目标是让命令行变得友好。安装好就能用,不需要任何 .fishrc 配置文件。它会: - 你打字时实时高亮语法(红色 = 错误,蓝色 = 正确命令) - 基于你的历史记录,灰色显示自动建议(按 → 采纳) - Tab 补全时显示每个选项的描述
核心概念表¶
| 概念 | 说明 | Bash/Zsh 等价物 |
|---|---|---|
| 自动建议 | 基于历史的灰色补全文本 | zsh-autosuggestions 插件 |
| 语法高亮 | 实时命令着色 | zsh-syntax-highlighting 插件 |
| Universal 变量 | 跨所有 Fish 会话共享的变量 | 无等价物 |
| Abbreviation | 输入缩写自动展开为完整命令 | alias(但不展开) |
| Function | Fish 的函数/命令定义 | Bash function |
| Completion | 命令补全定义 | Bash completion |
| Fisher | Fish 的插件管理器 | Oh My Zsh |
conf.d | 配置片段目录 | .bashrc.d/ |
Fish 语法 vs Bash 语法速查¶
| 功能 | Bash | Fish |
|---|---|---|
| 变量赋值 | VAR=value | set VAR value |
| 导出变量 | export VAR=value | set -x VAR value |
| 条件判断 | if [ "$x" = "y" ]; then ... fi | if test "$x" = "y" ... end |
| 循环 | for i in 1 2 3; do ... done | for i in 1 2 3 ... end |
| 命令替换 | `cmd` 或 $(cmd) | (cmd) |
| 函数 | function f() { ... } | function f ... end |
| 逻辑与 | cmd1 && cmd2 | cmd1; and cmd2 或 cmd1 && cmd2(3.0+) |
| 逻辑或 | cmd1 \|\| cmd2 | cmd1; or cmd2 或 cmd1 \|\| cmd2(3.0+) |
| 字符串切片 | ${var:0:3} | string sub -l 3 $var |
安装配置¶
安装方式¶
macOS
Ubuntu/Debian
Arch Linux
Fedora
设为默认 Shell¶
# 添加 fish 到允许的 shell 列表
echo $(which fish) | sudo tee -a /etc/shells
# 设为默认 shell
chsh -s $(which fish)
# 重新登录后生效
如果不想改默认 Shell,可以在终端模拟器的配置中设置启动命令为 fish。
配置文件结构¶
~/.config/fish/
├── config.fish # 主配置文件 (类似 .bashrc)
├── fish_variables # Universal 变量存储 (自动管理)
├── functions/ # 自定义函数
│ ├── fish_prompt.fish # 提示符定义
│ └── my_func.fish # 自定义函数
├── completions/ # 自定义补全
│ └── my_tool.fish
└── conf.d/ # 配置片段 (按字母序加载)
├── aliases.fish
└── env.fish
基础配置¶
# ~/.config/fish/config.fish
# 环境变量
set -x EDITOR helix
set -x LANG en_US.UTF-8
# PATH 添加(Fish 方式)
fish_add_path ~/go/bin
fish_add_path ~/.local/bin
fish_add_path ~/.cargo/bin
# 关闭欢迎语
set -g fish_greeting ""
# 别名
alias ll "ls -la"
alias g "git"
alias k "kubectl"
# 缩写(更推荐用 abbreviation)
abbr -a gs git status
abbr -a gc git commit
abbr -a gp git push
abbr -a gd git diff
Web 配置界面¶
Fish 提供了一个浏览器配置界面:
快速上手¶
自动建议¶
Fish 最显著的功能是基于历史的自动建议:
# 你开始输入:
git com
# Fish 自动显示灰色建议:
git commit -m "上次的提交消息"
# 按 → 或 Ctrl+F 采纳整个建议
# 按 Alt+→ 采纳建议中的下一个单词
语法高亮¶
# 输入正确的命令 — 蓝色/绿色
git status
# 输入不存在的命令 — 红色
gti status # ← 红色,提示你打错了
# 文件路径存在 — 下划线
cat ~/.config/fish/config.fish # ← 有下划线
# 文件路径不存在 — 无下划线
cat /nonexistent/file # ← 无下划线
Tab 补全¶
# 补全命令
git ch<Tab>
# 显示:
# checkout (Switch branches or restore working tree files)
# cherry (Find commits yet to be applied to upstream)
# cherry-pick (Apply the changes introduced by some existing commits)
# 补全文件路径
cat ~/Do<Tab>
# ~/Documents/ ~/Downloads/
# 补全命令选项
git log --<Tab>
# --oneline (Show each commit as a single line)
# --graph (Draw a text-based graphical representation)
# --all (Pretend as if all the refs are listed)
# ... 每个选项都有描述!
历史搜索¶
# Ctrl+R — 进入历史搜索模式
# 输入关键词,模糊匹配历史命令
# 上下箭头选择
# Enter 执行
# 上下箭头 — 基于当前输入前缀搜索
git <Up>
# 只显示以 "git" 开头的历史命令
变量操作¶
# 设置变量
set name "World"
echo "Hello, $name"
# 列表变量(Fish 原生支持列表)
set fruits apple banana cherry
echo $fruits[1] # apple
echo $fruits[2..3] # banana cherry
echo (count $fruits) # 3
# Universal 变量(跨会话持久化)
set -U MY_TOKEN "abc123"
# 在任何 Fish 会话中都可访问,不需要写入配置文件
进阶用法¶
Abbreviation 缩写¶
缩写比 alias 更好——它会在你按空格或回车时展开为完整命令:
# 添加缩写
abbr -a gst git status
abbr -a gco git checkout
abbr -a gcm git commit -m
abbr -a gp git push
abbr -a gl git log --oneline --graph
# 使用时
gst<Space>
# 自动展开为: git status
# 这意味着历史记录中保存的是完整命令,方便搜索
# 查看所有缩写
abbr
# 删除缩写
abbr -e gst
自定义函数¶
Fish 的函数就是命令。每个函数保存在单独的文件中,按需自动加载:
# 创建函数(方式一:命令行定义 + 保存)
function mkcd
mkdir -p $argv[1]
cd $argv[1]
end
funcsave mkcd
# 自动保存到 ~/.config/fish/functions/mkcd.fish
# 创建函数(方式二:直接写文件)
# ~/.config/fish/functions/backup.fish
function backup --description "Backup a file with timestamp"
set filename $argv[1]
set timestamp (date +%Y%m%d_%H%M%S)
cp $filename "$filename.bak.$timestamp"
echo "Backed up to $filename.bak.$timestamp"
end
自定义提示符¶
# ~/.config/fish/functions/fish_prompt.fish
function fish_prompt
set -l last_status $status
set -l cwd (basename (prompt_pwd))
set -l git_branch (git branch --show-current 2>/dev/null)
# 用户名@主机名
set_color cyan
echo -n (whoami)
set_color normal
echo -n " "
# 当前目录
set_color blue
echo -n $cwd
set_color normal
# Git 分支
if test -n "$git_branch"
set_color yellow
echo -n " ($git_branch)"
set_color normal
end
# 上一个命令的状态
if test $last_status -ne 0
set_color red
echo -n " [$last_status]"
set_color normal
end
echo -n " > "
end
Fisher 插件管理器¶
# 安装 Fisher
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher
# 安装插件
fisher install PatrickF1/fzf.fish # fzf 集成
fisher install jethrokuan/z # z 目录跳转
fisher install IlanCosman/tide@v6 # Tide 提示符主题
fisher install jorgebucaran/nvm.fish # Node 版本管理
# 列出已安装插件
fisher list
# 更新所有插件
fisher update
# 删除插件
fisher remove PatrickF1/fzf.fish
条件和循环¶
# if/else
if test -f config.toml
echo "Config found"
else if test -f config.json
echo "JSON config found"
else
echo "No config found"
end
# switch/case
switch (uname)
case Linux
echo "Linux"
case Darwin
echo "macOS"
case '*'
echo "Unknown OS"
end
# for 循环
for file in *.py
echo "Processing $file"
python $file
end
# while 循环
while read -l line
echo "Line: $line"
end < input.txt
与 Bash 脚本的兼容¶
Fish 不是 POSIX 兼容的,但有几种处理方式:
# 方式一:用 bash 执行 Bash 脚本
bash script.sh
# 方式二:在 Fish 中使用 bash -c
bash -c 'for i in {1..5}; do echo $i; done'
# 方式三:脚本文件的 shebang 用 bash
#!/bin/bash # 脚本文件仍然用 bash 解释器
# 方式四:source 时指定 bash
bass source /etc/profile # 需要安装 bass 插件
fisher install edc/bass
常见问题¶
Q1: Fish 不兼容 POSIX,会有问题吗?¶
实际影响比想象的小: - Shell 脚本(.sh 文件)仍然用 bash 执行,不受影响 - CI/CD 配置中的脚本通常指定 bash,不受影响 - 影响的只是你在交互式终端中输入的命令语法 - 一些 .bashrc 中的 source 命令需要转换
Q2: nvm/conda/pyenv 在 Fish 中不工作?¶
这些工具通常需要 Bash/Zsh 语法的初始化脚本。解决方案:
# conda
# conda 原生支持 fish
conda init fish
# nvm — 使用 fisher 插件
fisher install jorgebucaran/nvm.fish
# pyenv — 使用 Fish 原生支持
set -x PYENV_ROOT $HOME/.pyenv
fish_add_path $PYENV_ROOT/bin
pyenv init - fish | source
Q3: 如何让团队共享的 Shell 脚本同时兼容 Bash 和 Fish?¶
不要让它们兼容。团队脚本用 Bash 编写(#!/bin/bash),Fish 作为交互式 Shell 使用。这是最实际的方案。
Q4: Fish 的性能如何?¶
Fish 的启动速度和响应速度通常优于配置了大量插件的 Zsh。因为 Fish 的功能是 C++ 实现的内置功能,而不是 Shell 脚本层面的插件。
Q5: 如何从 Zsh 迁移到 Fish?¶
# 1. 安装 Fish,先不设为默认 Shell
brew install fish
# 2. 在终端中手动进入 fish 体验
fish
# 3. 逐步迁移配置
# - 环境变量:set -x 代替 export
# - PATH:fish_add_path 代替手动拼接
# - alias:改用 abbr(更推荐)
# - 适应 1-2 周后再设为默认 Shell
Q6: 如何让 Fish 提示符好看?¶
最简单的方式是安装 Tide 或 Starship:
# Tide(Fish 原生主题,功能丰富)
fisher install IlanCosman/tide@v6
tide configure # 交互式配置
# Starship(跨 Shell 提示符,配置在 starship.toml)
brew install starship
echo 'starship init fish | source' >> ~/.config/fish/config.fish
参考资源¶
| 资源 | 链接 |
|---|---|
| 官方网站 | https://fishshell.com |
| GitHub 仓库 | https://github.com/fish-shell/fish-shell |
| 官方文档 | https://fishshell.com/docs/current/ |
| 教程 | https://fishshell.com/docs/current/tutorial.html |
| Fisher 插件管理器 | https://github.com/jorgebucaran/fisher |
| Awesome Fish | https://github.com/jorgebucaran/awsm.fish |
| Tide 提示符主题 | https://github.com/IlanCosman/tide |
总结:Fish 是"不想折腾配置"的开发者的最佳 Shell 选择。如果你花了太多时间配置 Zsh 插件,或者想要一个新手友好的终端体验,Fish 安装即用的设计理念会让你感到解脱。唯一的代价是学习略有不同的语法,但交互式使用中这个差异很快就能适应。