























记录从零搭建服务器 zsh 环境的过程,包含踩过的坑。配置已整理成 dotfiles 仓库,新服务器一键部署。
环境:Ubuntu 24.04 / Debian 12,zsh + Zim 框架
最终工具栈:
| 工具 | 作用 |
|---|---|
| Zim | zsh 框架,比 oh-my-zsh 轻,启动快 |
| Powerlevel10k | 提示符主题 |
| eza | 带图标的 ls 替代,支持 git 状态列 |
| zoxide | 智能目录跳转,记住历史,z 关键词 直达 |
| fzf | 模糊查找,Ctrl+R 历史搜索、Ctrl+T 文件选择 |
| bat | 带语法高亮的 cat(Debian/Ubuntu 上命令名为 batcat) |
先做这一步,否则 eza 图标会显示成方块。
eza 的图标由本地终端渲染,服务器不需要装字体。SSH 连服务器时,字符渲染在本地做,改的是本地终端的字体配置。
下载 0xProto Nerd Font:nerdfonts.com
装完在终端设置里切换字体。Windows Terminal 注意:每个 profile 可以单独设字体,改的要是你 SSH 用的那个 profile,不是默认 profile。
.zshenv比 /etc/zsh/zshrc 更早加载,用来解决 compinit 重复初始化的问题(见下方坑点记录)。
.zimrc
# 基础模块 zmodule environment zmodule input zmodule termtitle zmodule utility # Git zmodule git zmodule wfxr/forgit # fzf 加持的 git 交互工具 # 主题 zmodule duration-info zmodule git-info zmodule romkatv/powerlevel10k # 补全(必须在普通模块之后) zmodule zsh-users/zsh-completions --fpath src zmodule completion # 必须最后加载 zmodule zsh-users/zsh-syntax-highlighting zmodule zsh-users/zsh-history-substring-search zmodule zsh-users/zsh-autosuggestions
.zshrc
# ========================= # ZSH CONFIG # ========================= # p10k instant prompt(必须最顶部) if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" fi # Zim 初始化 ZIM_HOME=${ZDOTDIR:-$HOME}/.zim source ${ZIM_HOME}/init.zsh # 基础环境变量 export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 export EDITOR=vim export VISUAL=vim # 历史记录 HISTSIZE=50000 SAVEHIST=50000 HISTFILE=~/.zsh_history setopt APPEND_HISTORY setopt SHARE_HISTORY # 跨会话共享 setopt HIST_IGNORE_ALL_DUPS # 去重 setopt HIST_REDUCE_BLANKS setopt HIST_IGNORE_SPACE # 空格开头不入历史(用于敏感命令) # 补全 setopt AUTO_MENU setopt COMPLETE_IN_WORD # 目录 setopt AUTO_CD # p10k 主题 [[ -f ~/.p10k.zsh ]] && source ~/.p10k.zsh # ---------------------------- # Aliases # ---------------------------- # eza alias ls='eza --icons --group-directories-first' alias ll='eza -alF --icons --group-directories-first --git --header --time-style=relative' alias la='eza -A --icons --group-directories-first' alias l='eza --icons --group-directories-first' alias lt='eza --tree --icons --level=2' alias lt3='eza --tree --icons --level=3' # bat alias cat='batcat --paging=never' alias bat='batcat' # 目录跳转 alias ..='cd ..' alias ...='cd ../..' # 危险操作加确认(服务器上手滑代价大) alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' # grep alias grep='grep --color=auto' # Git alias gs='git status' alias ga='git add' alias gc='git commit' alias gp='git push' alias gl='git log --oneline --graph --decorate' # Docker alias dps='docker ps' alias dpa='docker ps -a' alias di='docker images' alias dlog='docker logs -f' alias dex='docker exec -it' # Systemd alias sctl='systemctl' alias sstart='systemctl start' alias sstop='systemctl stop' alias sstat='systemctl status' alias srestart='systemctl restart' # Network alias ports='ss -tulnp' alias myip='curl -s --max-time 5 ifconfig.me' # Process alias psg='ps aux | grep' # ---------------------------- # 性能 # ---------------------------- export KEYTIMEOUT=1 # ---------------------------- # 函数 # ---------------------------- # mkdir + cd mkcd() { mkdir -p "$1" && cd "$1" } # 通用解压 extract() { if [ ! -f "$1" ]; then echo "'$1' is not a valid file"; return 1; fi case "$1" in *.tar.bz2) tar xjf "$1" ;; *.tar.gz) tar xzf "$1" ;; *.tar.xz) tar xJf "$1" ;; *.tar) tar xf "$1" ;; *.bz2) bunzip2 "$1" ;; *.gz) gunzip "$1" ;; *.zip) unzip "$1" ;; *.7z) 7z x "$1" ;; *.rar) unrar x "$1" ;; *) echo "Unknown format: $1" ;; esac } # ---------------------------- # 工具初始化(放文件末尾) # ---------------------------- eval "$(zoxide init zsh)" source <(fzf --zsh)
表现
warning: completion was already initialized before completion module. Will call compinit again.
同时 p10k 弹 instant prompt 警告——p10k 只是信使,检测到启动期间有输出就报警,真正的根因是上面那行。
根因
Ubuntu/Debian 的 /etc/zsh/zshrc 在系统层面调了一次 compinit,Zim 的 completion 模块又调一次,重复了。
定位方法:
zsh -xic exit 2>&1 | grep "compinit" | head
输出里能看到 +/etc/zsh/zshrc:111> compinit,确认是系统配置先调的。
解法
创建 ~/.zshenv,写入:
.zshenv 比 /etc/zsh/zshrc 更早加载。Debian/Ubuntu 的系统 zshrc 在调 compinit 前会检查这个变量,值为 1 就跳过。Zim 的 completion 模块成为唯一一次初始化,警告消失。
验证系统是否支持该变量:
grep -n "skip_global_compinit" /etc/zsh/zshrc
有输出即支持。
表现
/home/csr/.zimrc:4: command not found: ^M fatal: unable to access 'https://github.com/zimfw/environment?.git/': URL rejected: Malformed input to a URL function
^M 是 Windows 的回车符 \r。zmodule environment 变成了 zmodule environment\r,git clone 的 URL 里带了非法字符。
根因
在 Windows 上用编辑器(VS Code 默认)保存文件时,换行符是 CRLF(\r\n),Linux/zsh 需要 LF(\n)。
三层解法
CRLF 切成 LF 再保存.gitattributes,强制所有文件 LF
* text=auto eol=lf *.sh text eol=lf .zshrc text eol=lf .zimrc text eol=lf .zshenv text eol=lf
提交时加一步规范化:
deploy() 函数里用 sed 在落盘前清洗
sed 's/\r$//' "$src" > "$dst"
最后一道兜底,即使仓库里偶尔带了 \r,落到目标机器也是干净的。
| bash 执行表现
You must use zsh to run install.zsh curl: (23) Failure writing output to destination
根因
Zim 的安装脚本是 .zsh 文件,必须用 zsh 执行。| bash 会把脚本内容塞给 bash,bash 检测到不是 zsh 就退出,curl 那端管道断了所以报写入失败。
解法
先下载到临时文件,再明确用 zsh 执行:
curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh \ -o /tmp/zim-install.zsh zsh /tmp/zim-install.zsh rm -f /tmp/zim-install.zsh
配置托管在 GitHub:github.com/CasearF/dotfiles
新服务器部署:
git clone https://github.com/CasearF/dotfiles.git ~/dotfiles cd ~/dotfiles && chmod +x install.sh && ./install.sh exec zsh
install.sh 自动完成:zsh/git/curl/unzip 检测安装 → eza/zoxide/fzf/bat 安装 → Zim 安装 → 旧配置备份(.bak-时间戳)→ 配置部署(自动清 CRLF)→ Zim 模块安装 → 设 zsh 为默认 shell。
exec zsh ll # eza:图标 + 目录排前 + git 列 z some-dir # zoxide:模糊跳转 Ctrl+R # fzf:历史模糊搜索 bat ~/.zshrc # bat:语法高亮
启动无 compinit 警告,五项全绿即完工。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。