从 Fish 迁移到 Zsh:配置与函数转换

Fish → Zsh 迁移笔记

记录了从 fish shell 逐步迁移配置到 zsh 的过程和经验。

目录

  1. Starship 多 shell 独立配置
  2. 环境变量同步
  3. x-cmd 跨 shell 支持
  4. 函数翻译对照
  5. 缩写/别名翻译
  6. 清理提示

Starship 多 shell 独立配置

原理

Starship 默认读 ~/.config/starship.toml,所有 shell 共用。通过 STARSHIP_CONFIG 环境变量让不同 shell 各用各的配置。

步骤

  1. 复制出多份配置:
1
2
cp ~/.config/starship.toml ~/.config/starship-fish.toml
cp ~/.config/starship.toml ~/.config/starship-zsh.toml
  1. 在各 shell rc 中指定配置:

Fish (~/.config/fish/config.fish):

1
2
set -gx STARSHIP_CONFIG ~/.config/starship-fish.toml
starship init fish | source

Zsh (~/.zshrc):

1
2
export STARSHIP_CONFIG=~/.config/starship-zsh.toml
eval "$(starship init zsh)"

关键点

  • STARSHIP_CONFIG 必须在 starship init 之前设置
  • Starship 配置查找优先级:$STARSHIP_CONFIG~/.config/starship.toml
  • 设了 STARSHIP_CONFIG 后,旧的 starship.toml 不再被读取

配色快速替换

不改布局只换色,只需改 [palettes.colors] 里的十六进制色值。

已知问题:分隔符空白

Starship 的 format 字符串中分隔符写死,但中间的模块可能为空(如 git 不在仓库时、语言版本不匹配时),导致  双分隔符。无优雅解法,要么忍受要么用 $all 自动格式(但会失去定制的渐变效果)。


环境变量同步

Fish 的 set -gx 对应 Zsh 的 export

Fish Zsh
set -gx VAR value export VAR=value
set -gx PATH $PATH /new/path export PATH="$PATH:/new/path"
set -p PATH ~/.local/bin export PATH="$HOME/.local/bin:$PATH"

需要同步的变量直接从 ~/.config/fish/config.fish 搜索 set -gx,翻译后加到 .zshrc

1
2
3
export TERMINFO_DIRS=/usr/lib/kitty/terminfo:/usr/share/terminfo
export EZA_COLORS="di=38;2;122;162;247:..."
export PATH="$PATH:./node_modules/.bin"

x-cmd 跨 shell 支持

x-cmd 核心是 bash 脚本(~/.x-cmd.root/bin/___x_cmdexe_exp),fish 通过 rc 文件包装。zsh 需要手动创建包装函数和 PATH。

添加到 .zshrc

1
2
3
4
5
6
7
8
9
# PATH
export PATH="$HOME/.x-cmd.root/local/data/pkg/sphere/X/l/j/h/bin:$HOME/.x-cmd.root/bin:$PATH"

# 包装函数
x() {
___X_CMD_REAL_CALLER_SHELL=zsh \
___X_CMD_RUNMODE=9 \
bash "$HOME/.x-cmd.root/bin/___x_cmdexe_exp" "$@"
}

关键环境变量

  • ___X_CMD_REAL_CALLER_SHELL=zsh — 告诉 x-cmd 当前是哪个 shell
  • ___X_CMD_RUNMODE=9 — 交互模式
  • fish 的 set -g ___X_CMD_REAL_CALLER_SHELL fish 只在 fish 会话内有效,zsh 看不到

函数翻译对照

Fish 函数定义翻译为 Zsh 函数:

Fish Zsh
function name name() { ... }
$argv "$@"
command foo $argv command foo "$@"
if not set -q VAR if [ -z "$VAR" ]
`eval … source`
builtin cd -- "$dir" builtin cd -- "$dir"
and / or && / `

示例:cat → bat

1
2
3
function cat
command bat $argv
end

1
cat() { command bat "$@"; }

陷阱:别名与函数冲突

oh-my-zsh 预设了一些别名(如 alias ls='ls --color=tty'),zsh 不允许函数和别名同名。定义同名函数前必须先 unalias

1
2
3
4
5
6
# ❌ 报错:defining function based on alias `ls' / parse error near `()'
ls() { command eza --icons "$@"; }

# ✅ 先清除别名再定义
unalias ls 2>/dev/null
ls() { command eza --icons "$@"; }

也适用于 catgrep 等 oh-my-zsh 可能预设别名的命令。

示例:yazi 文件管理器

1
2
3
4
5
6
7
8
function y
set tmp (mktemp -t "yazi-cwd.XXXXXX")
yazi $argv --cwd-file="$tmp"
if read -z cwd < "$tmp"; and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
builtin cd -- "$cwd"
end
rm -f -- "$tmp"
end

1
2
3
4
5
6
7
8
9
y() {
local tmp=$(mktemp -t "yazi-cwd.XXXXXX")
yazi "$@" --cwd-file="$tmp"
local cwd=$(cat "$tmp")
if [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
builtin cd -- "$cwd"
fi
rm -f -- "$tmp"
}

示例:conda 按需加载

Fish 用 conda shell.fish hook,Zsh 用 conda shell.zsh hook

1
2
3
4
5
6
7
8
conda-on() {
if [ -z "$CONDA_SHLVL" ]; then
eval "$(/opt/miniconda3/bin/conda shell.zsh hook)"
echo "conda 已激活"
else
echo "conda 已经在运行中"
fi
}

示例:中文函数名

1
2
3
安装() { command yay -S "$@"; }
滚() { sysup; }
卸载() { command yay -Rns "$@"; }

缩写/别名翻译

Fish 的 abbr(输入后展开)Zsh 没有原生等价物,用 alias 近似替代:

Fish abbr Zsh alias
abbr grub 'LANGUAGE=... sudo grub-mkconfig ...' alias grub='LANGUAGE=... sudo ...'
abbr fa fastfetch alias fa='fastfetch'
abbr reboot 'systemctl reboot' alias reboot='systemctl reboot'
abbr lsfg 'LSFG_PROCESS="miyu"' alias lsfg='export LSFG_PROCESS=miyu'

区别:fish 的 abbr 输入后按空格才展开,可以预览后再确认;zsh 的 alias 直接替换执行,没有预览步骤。


终端状态管理

问题

fish 在每次命令执行后自动恢复终端设置。zsh 不会,子进程留下的”脏”终端状态会直接继承。

典型场景:sl(蒸汽火车动画)通过管道传给 lolcat(彩虹色)后,sl 检测到 stdout 不是 TTY,行为异常,退出时没有恢复 raw 模式等终端设置。zsh 继承了这种脏状态,导致后续命令(如 shorin)的输出列对齐错乱。

解决

对有副作用的命令,函数内手动恢复终端:

1
2
3
4
5
# ❌ 终端状态可能被污染
sl() { command sl | lolcat; }

# ✅ 执行后恢复终端
sl() { command sl | lolcat; tput sgr0; stty sane 2>/dev/null; }
恢复命令 作用
tput sgr0 重置文字颜色、粗体等显示属性
stty sane 恢复终端行设置(raw/cooked、回显等)

两者都不清屏。

通用建议

迁移任何涉及终端控制的命令(动画、TUI、管道穿过色工具)时,在 zsh 包装函数末尾加上 tput sgr0; stty sane 2>/dev/null。这能避免终端状态污染导致后续命令输出错乱(如 column 列对齐失败)。

注意:fish 这类问题不会出现,因为它每次命令后自动恢复终端。zsh 需要手动处理。


清理提示

写完配置后检查语法:

1
zsh -n ~/.zshrc

无输出 = 语法正确。生效:exec zsh


从 Fish 迁移到 Zsh:配置与函数转换
https://tingfeng347.github.io/2026/03/17/从 Fish 迁移到 Zsh:配置与函数转换/
作者
Tingfeng
发布于
2026年3月17日
许可协议