Fish Shell 介绍、配置与扩展指南

引言

作为一名开发者,每天都需要在终端中工作,那么选择一个高效、智能的 shell 环境将会极大地提升生产力。之前使用的是 zsh, zsh 这需要自己进行大量的定制化配置才能满足自动补全, 语法高亮等效果. 但是在厌倦一次又一次的配置(CTRL+C + CTRL+V)之后,我尝试了 fish, 它开箱即用的特点, 让我瞬间爱上了它.

那么开始正式介绍: fish shell(Friendly Interactive Shell)

什么是 Fish Shell?

Fish shell 是一个用户友好的命令行 shell,它的设计理念是"开箱即用"(works out of the box)。与传统的 bash 或 zsh 相比,fish 提供了许多现代化的特性,让命令行操作变得更加直观和高效。

为什么选择 Fish?

Fish shell 相比传统 shell 有以下几个突出优势:

智能自动补全:Fish 提供了基于历史记录和手册页的智能自动补全功能。当你输入命令时,它会实时显示可能的补全选项,并用灰色文字提示。你只需按下右箭头键或 Tab 键就能接受建议。

语法高亮:Fish 会实时对你输入的命令进行语法高亮。正确的命令显示为绿色,错误的命令显示为红色,这让你能立即发现拼写错误或语法问题。

Web 配置界面:Fish 提供了一个基于 Web 的配置界面,你可以通过运行 fish_config 命令来打开它。在这个界面中,你可以轻松地选择主题、配置提示符、设置颜色方案等。

更简洁的脚本语法:Fish 的脚本语法更加现代和直观。例如,它使用 andor 代替了 &&||,使用 end 来结束代码块而不是各种括号。

安装 Fish Shell

Fish shell 的安装非常简单,以下是不同操作系统的安装方法:

macOS

# 使用 Homebrew
brew install fish

# 将 fish 设为默认 shell
echo /usr/local/bin/fish | sudo tee -a /etc/shells
chsh -s /usr/local/bin/fish

Ubuntu/Debian

# 添加官方 PPA
sudo apt-add-repository ppa:fish-shell/release-4
sudo apt update
sudo apt install fish

# 设为默认 shell
chsh -s /usr/bin/fish

基础配置

Fish 的配置文件位于 ~/.config/fish/config.fish。与 bash 的 .bashrc 类似,这个文件会在每次启动 fish 时执行。

创建配置文件

# 创建配置目录
mkdir -p ~/.config/fish

# 创建配置文件
touch ~/.config/fish/config.fish

基础配置示例

# ~/.config/fish/config.fish

# 设置编辑器
set -gx EDITOR vim

# 设置 PATH
set -gx PATH $HOME/.local/bin $PATH

# 设置别名
alias ll "ls -lah"
alias g "git"
alias dc "docker compose"
alias k "kubectl"

# 自定义欢迎消息
function fish_greeting
    echo "Welcome to Fish Shell!"
    echo "Today is" (date "+%Y-%m-%d %H:%M:%S")
end

# 设置代理(如果需要)
# 但是推荐参考文章末尾的 functions, 而不在 config.fish 中定义函数了.
function proxy_on
    set -gx http_proxy http://127.0.0.1:7890
    set -gx https_proxy http://127.0.0.1:7890
    echo "Proxy enabled"
end

function proxy_off
    set -e http_proxy
    set -e https_proxy
    echo "Proxy disabled"
end

Fisher - Fish 的包管理器

Fisher 是 fish shell 的插件管理器,它让安装和管理 fish 插件变得极其简单。Fisher 的设计理念是极简和高效,它没有外部依赖,安装速度快,配置简单。

安装 Fisher

安装 Fisher 只需要一行命令:

curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher

Fisher 基本使用

Fisher 的使用非常直观,以下是常用命令:

# 安装插件
fisher install ilancosman/tide@v6

# 列出已安装的插件
fisher list

# 更新插件
fisher update

# 删除插件
fisher remove ilancosman/tide

# 从文件安装插件列表
fisher install < fish_plugins

管理插件列表

Fisher 会自动维护一个 ~/.config/fish/fish_plugins 文件,记录所有已安装的插件。你可以将这个文件加入版本控制,方便在不同机器上同步配置:

# 保存当前插件列表
fisher list > ~/.config/fish/fish_plugins

# 在新机器上恢复插件
fisher install < ~/.config/fish/fish_plugins

推荐的 Fish 扩展

以下是一些提升 fish shell 体验的优秀扩展:

更多的 fish 拓展: https://github.com/topics/fish

1. Z - 智能目录跳转

Z 是一个纯 fish 实现的智能目录跳转工具。它会记录你访问过的目录,让你能够快速跳转到常用目录。

# 安装
fisher install jethrokuan/z

# 使用示例
cd ~/projects/my-website  # 第一次需要完整路径
z website                 # 之后只需输入部分名称
z proj                    # 模糊匹配

Z 的工作原理是通过记录你访问目录的频率和时间,使用智能算法计算出最可能的目标目录。使用一段时间后,它会变得越来越智能。

2. Tide - 现代化的 Fish 提示符

Tide 是一个功能丰富、高度可定制的 Fish 提示符主题。

# 安装
fisher install ilancosman/tide@v6

# 运行配置向导
tide configure

Tide 提供了多种提示符样式,支持显示 Git 状态、执行时间、Node.js 版本、Python 虚拟环境等信息。

3. FZF - 模糊查找集成

FZF.fish 将强大的 fzf 模糊查找工具集成到 fish 中。

# 先安装 fzf
brew install fzf  # macOS
# 或
sudo apt install fzf  # Ubuntu

# 安装 fish 集成
fisher install PatrickF1/fzf.fish

安装后,你可以使用以下快捷键:

  • Ctrl+R:搜索命令历史
  • Ctrl+Alt+F:搜索文件
  • Ctrl+Alt+L:搜索 Git 日志
  • Ctrl+V:搜索环境变量

4. Autopair - 自动配对括号

Autopair 自动为你输入的括号、引号等添加配对符号。

# 安装
fisher install jorgebucaran/autopair.fish

5. Done - 长时间命令完成通知

Done 会在长时间运行的命令完成后发送桌面通知。

# 安装
fisher install franciscolourenco/done

# 默认超过 5 秒的命令会触发通知
# 可以通过设置环境变量来调整
set -U __done_min_cmd_duration 3000  # 3 秒

6. Colored Man Pages - 彩色手册页

Colored Man Pages 为 man 手册页添加语法高亮。

# 安装
fisher install decors/fish-colored-man

7. Puffer Fish - Git 文本扩展

Puffer Fish 提供了 Git 命令的文本扩展功能。

# 安装
fisher install nickeb96/puffer-fish

# 使用示例
# 输入 .. 后按空格会扩展为 ../
# 输入 !! 后按空格会扩展为上一条命令
# 输入 !$ 后按空格会扩展为上一条命令的最后一个参数

实用技巧和最佳实践

1. 函数管理(functions)

Fish 中的函数可以保存为独立文件,放在 ~/.config/fish/functions/ 目录下:

# ~/.config/fish/functions/mkcd.fish
function mkcd -d "创建目录并进入"
    mkdir -p $argv[1]
    cd $argv[1]
end

# 使用
mkcd new-project

2. 条件配置

根据不同环境加载不同配置:

# ~/.config/fish/config.fish

# 根据操作系统加载不同配置
switch (uname)
    case Darwin
        source ~/.config/fish/macos.fish
    case Linux
        source ~/.config/fish/linux.fish
end

# 根据主机名加载特定配置
if test (hostname) = "work-laptop"
    source ~/.config/fish/work.fish
end

3. 与 tmux 集成

~/.config/fish/config.fish 中将内容更改为下面这样:

# 自动启动 tmux
if status is-interactive
    and not set -q TMUX
    exec tmux new-session -A -s main
end

从 Bash/Zsh 迁移到 Fish

如果你正在从 bash 或 zsh 迁移,以下是一些需要注意的差异:

语法差异

变量设置

# Bash/Zsh
export PATH=$HOME/bin:$PATH

# Fish
set -gx PATH $HOME/bin $PATH

条件判断

# Bash/Zsh
if [ -f ~/.config ]; then
    echo "File exists"
fi

# Fish
if test -f ~/.config
    echo "File exists"
end

循环

# Bash/Zsh
for i in 1 2 3; do
    echo $i
done

# Fish
for i in 1 2 3
    echo $i
end

命令替换

# Bash/Zsh
echo "Today is $(date)"

# Fish
echo "Today is "(date)

迁移技巧

  1. 使用 bass 运行 bash 脚本
# 安装 bass
fisher install edc/bass

# 运行 bash 脚本
bass source ~/.bashrc
  1. 转换环境变量
# 创建转换函数
function import_bash_env
    bash -c 'source ~/.bashrc && env' | while read line
        set var (echo $line | cut -d= -f1)
        set val (echo $line | cut -d= -f2-)
        set -gx $var $val
    end
end

个人常用 fish functions 分享

快捷的 temrinal 临时代理配置
存放位置: ~/.config/fish/functions/proxy.fish

function proxy --description 'Manage HTTP/HTTPS proxy settings'
    # 默认代理配置
    set -l default_proxy "http://127.0.0.1:7897"
    
    # 解析参数
    argparse 'h/help' 'u/unset' 's/show' 'p/port=' 'H/host=' 't/test' -- $argv
    or return 1
    
    # 显示帮助信息
    if set -q _flag_help
        echo "Usage: proxy [OPTIONS] [PROXY_URL]"
        echo ""
        echo "Manage HTTP/HTTPS proxy settings"
        echo ""
        echo "Options:"
        echo "  -u, --unset          Unset proxy environment variables"
        echo "  -s, --show           Show current proxy configuration"
        echo "  -H, --host HOST      Set proxy host (default: 127.0.0.1)"
        echo "  -p, --port PORT      Set proxy port (default: 7897)"
        echo "  -t, --test           Test proxy connection after setup"
        echo "  -h, --help           Show this help message"
        echo ""
        echo "Examples:"
        echo "  proxy                                    # Set default proxy (127.0.0.1:7897)"
        echo "  proxy --test                             # Set default proxy and test connection"
        echo "  proxy http://proxy.example.com:8080      # Set custom proxy URL"
        echo "  proxy -H 192.168.1.1 -p 8080 --test     # Set proxy with host/port and test"
        echo "  proxy --show                             # Show current proxy settings"
        echo "  proxy --unset                            # Remove proxy settings"
        return 0
    end
    
    # 显示当前代理配置
    if set -q _flag_show
        echo "Current Proxy Configuration:"
        if set -q http_proxy; or set -q https_proxy
            echo "  HTTP Proxy:  "(set -q http_proxy; and echo $http_proxy; or echo "(not set)")
            echo "  HTTPS Proxy: "(set -q https_proxy; and echo $https_proxy; or echo "(not set)")
            if set -q no_proxy
                echo "  No Proxy:    $no_proxy"
            end
        else
            echo "  No proxy configured"
        end
        return 0
    end
    
    # 取消代理设置
    if set -q _flag_unset
        set -e http_proxy
        set -e https_proxy
        set -e HTTP_PROXY
        set -e HTTPS_PROXY
        echo "✓ Proxy settings cleared"
        return 0
    end
    
    # 构建代理URL
    set -l proxy_url $default_proxy
    
    # 使用host和port参数
    if set -q _flag_host; or set -q _flag_port
        set -l host (set -q _flag_host; and echo $_flag_host; or echo "127.0.0.1")
        set -l port (set -q _flag_port; and echo $_flag_port; or echo "7897")
        
        # 验证端口号
        if not string match -qr '^\d+$' $port; or test $port -lt 1 -o $port -gt 65535
            echo "Error: Invalid port number '$port'. Must be 1-65535" >&2
            return 1
        end
        
        set proxy_url "http://$host:$port"
    else if test (count $argv) -ge 1
        set proxy_url $argv[1]
    end
    
    # 验证代理URL格式
    if not string match -qr '^https?://' $proxy_url
        echo "Warning: Proxy URL should start with http:// or https://"
        set proxy_url "http://$proxy_url"
    end
    
    # 提取主机和端口用于显示
    set -l proxy_host ""
    set -l proxy_port ""
    
    # 使用更精确的正则表达式解析URL
    if string match -qr '^https?://([^:/]+)(?::(\d+))?(?:/.*)?$' $proxy_url
        set proxy_host (string replace -r '^https?://([^:/]+)(?::(\d+))?(?:/.*)?$' '$1' $proxy_url)
        set proxy_port (string replace -r '^https?://([^:/]+)(?::(\d+))?(?:/.*)?$' '$2' $proxy_url)
    end
    
    # 设置环境变量(同时设置大小写版本以确保兼容性)
    set -gx http_proxy $proxy_url
    set -gx https_proxy $proxy_url
    set -gx HTTP_PROXY $proxy_url
    set -gx HTTPS_PROXY $proxy_url
    
    # 成功提示
    echo "✓ Proxy configured successfully"
    echo "  URL: $proxy_url"
    echo "  Host: $proxy_host"
    if test -n "$proxy_port"
        echo "  Port: $proxy_port"
    end
    
    # 仅在指定 --test 参数时测试代理连接
    if set -q _flag_test
        if command -q curl
            echo "Testing proxy connection..."
            # 使用Fish内置的方式处理超时,而不是依赖timeout命令
            if curl -s --max-time 5 --proxy $proxy_url http://httpbin.org/ip >/dev/null 2>&1
                echo "✓ Proxy connection test successful"
            else
                echo "⚠ Proxy connection test failed (proxy may be down or require authentication)"
            end
        else
            echo "⚠ curl command not found, cannot test proxy connection"
        end
    end
    
    # 提示常用的no_proxy设置
    if not set -q no_proxy
        echo ""
        echo "Tip: You may want to set no_proxy for local addresses:"
        echo "  set -gx no_proxy localhost,127.0.0.1,::1,.local"
    end
end

快捷的 terminal 临时配置 Claude Code
存放位置: ~/.config/fish/functions/setupClaudeCode.fish

function setupClaudeCode --description 'Configure Claude Code proxy with token and API endpoint'
    # 默认配置
    set -l default_token "[your api token]"
    set -l default_base_url "[your api endpoint]"
    
    # 解析参数
    argparse 'h/help' 'c/clear' 'token=' 'u/url=' 's/show' 't/test' -- $argv
    or return 1
    
    # 显示帮助信息
    if set -q _flag_help
        echo "Usage: setupClaudeCode [OPTIONS]"
        echo ""
        echo "Configure Claude Code proxy settings"
        echo ""
        echo "Options:"
        echo "  --token TOKEN        Set API token (default: masked)"
        echo "  -u, --url URL        Set base URL (default: $default_base_url)"
        echo "  -s, --show           Show current configuration"
        echo "  -c, --clear          Clear all Claude Code environment variables"
        echo "  -t, --test           Test connection after configuration"
        echo "  -h, --help           Show this help message"
        echo ""
        echo "Examples:"
        echo "  setupClaudeCode                           # Use default settings"
        echo "  setupClaudeCode --token your_token        # Set custom token"
        echo "  setupClaudeCode --token token -u https://api --test  # Set both and test"
        echo "  setupClaudeCode --show                    # Show current config"
        echo "  setupClaudeCode --clear                   # Clear configuration"
        return 0
    end
    
    # 显示当前配置
    if set -q _flag_show
        echo "Current Claude Code Configuration:"
        if set -q ANTHROPIC_AUTH_TOKEN
            echo "  ANTHROPIC_AUTH_TOKEN: "(string sub -l 8 $ANTHROPIC_AUTH_TOKEN)"..."
        else
            echo "  ANTHROPIC_AUTH_TOKEN: (not set)"
        end
        echo "  ANTHROPIC_BASE_URL: "(set -q ANTHROPIC_BASE_URL; and echo $ANTHROPIC_BASE_URL; or echo "(not set)")
        return 0
    end
    
    # 清除配置
    if set -q _flag_clear
        set -e ANTHROPIC_AUTH_TOKEN
        set -e ANTHROPIC_BASE_URL
        echo "✓ Claude Code environment variables cleared"
        return 0
    end
    
    # 设置变量值
    set -l api_token $default_token
    set -l base_url $default_base_url
    
    # 使用命令行参数覆盖默认值
    if set -q _flag_token
        set api_token $_flag_token
    else if test (count $argv) -ge 1
        set api_token $argv[1]
    end
    
    if set -q _flag_url
        set base_url $_flag_url
    else if test (count $argv) -ge 2
        set base_url $argv[2]
    end
    
    # 验证输入
    if test -z "$api_token"
        echo "Error: API token cannot be empty" >&2
        return 1
    end
    
    if test -z "$base_url"
        echo "Error: Base URL cannot be empty" >&2
        return 1
    end
    
    # 验证URL格式
    if not string match -q "http*" $base_url
        echo "Warning: Base URL should start with http:// or https://" >&2
    end
    
    # 设置环境变量
    set -gx ANTHROPIC_AUTH_TOKEN $api_token
    set -gx ANTHROPIC_BASE_URL $base_url
    
    # 成功提示
    echo "✓ Claude Code proxy configured successfully"
    echo "  Token(ANTHROPIC_AUTH_TOKEN): "(string sub -l 8 $api_token)"..."
    echo "  Base URL(ANTHROPIC_BASE_URL): $base_url"
    
    # 仅在指定 --test 参数时测试连接
    if set -q _flag_test
        if command -q curl
            echo "Testing connection..."
            if curl -s --max-time 5 "$base_url" >/dev/null 2>&1
                echo "✓ Connection test successful"
            else
                echo "⚠ Connection test failed (this might be normal if endpoint requires authentication)"
            end
        else
            echo "⚠ curl command not found, cannot test connection"
        end
    end
end

参考资源

祝你在 Fish Shell 的世界里游得愉快!🐟