跳转至

Fish Shell: 开箱即用的现代 Shell

为什么要学 Fish Shell

Bash 和 Zsh 是强大的 Shell,但它们有一个共同的问题:需要大量配置才能好用。Zsh 用户通常需要 Oh My Zsh + 一堆插件才能得到语法高亮、自动补全、历史搜索等基础功能。

Fish(Friendly Interactive SHell)的设计哲学是:这些功能不应该是插件,而应该是默认行为

功能BashZsh (需要插件)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(但不展开)
FunctionFish 的函数/命令定义Bash function
Completion命令补全定义Bash completion
FisherFish 的插件管理器Oh My Zsh
conf.d配置片段目录.bashrc.d/

Fish 语法 vs Bash 语法速查

功能BashFish
变量赋值VAR=valueset VAR value
导出变量export VAR=valueset -x VAR value
条件判断if [ "$x" = "y" ]; then ... fiif test "$x" = "y" ... end
循环for i in 1 2 3; do ... donefor i in 1 2 3 ... end
命令替换`cmd`$(cmd)(cmd)
函数function f() { ... }function f ... end
逻辑与cmd1 && cmd2cmd1; and cmd2cmd1 && cmd2(3.0+)
逻辑或cmd1 \|\| cmd2cmd1; or cmd2cmd1 \|\| cmd2(3.0+)
字符串切片${var:0:3}string sub -l 3 $var

安装配置

安装方式

macOS

brew install fish

Ubuntu/Debian

sudo apt-add-repository ppa:fish-shell/release-3
sudo apt update
sudo apt install fish

Arch Linux

pacman -S fish

Fedora

sudo dnf install fish

设为默认 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_config
# 浏览器会自动打开
# 可以配置:颜色主题、提示符样式、快捷键绑定等

快速上手

自动建议

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 Fishhttps://github.com/jorgebucaran/awsm.fish
Tide 提示符主题https://github.com/IlanCosman/tide

总结:Fish 是"不想折腾配置"的开发者的最佳 Shell 选择。如果你花了太多时间配置 Zsh 插件,或者想要一个新手友好的终端体验,Fish 安装即用的设计理念会让你感到解脱。唯一的代价是学习略有不同的语法,但交互式使用中这个差异很快就能适应。