我的終端運作速度很慢。每次打開新分頁,都要等一會兒才能開始輸入,非常煩人。我決定深入研究一下,在 Claude Code 的幫助下,啟動時間從 770 毫秒縮短到了 40 毫秒,提升了 19 倍。
倒不是我囤積太多工具了。我的 .zshrc 檔案裡大部分東西都是我需要的,但每多加一個都會增加 shell 的啟動時間。老實說,我之前沒研究過這個問題,就一直湊合著用。後來,John Lindquist 前幾天發了個帖子,我就想,不如讓 Claude Code 幫我加速一下。
{%embed https://x.com/johnlindquist/status/1993037322984866168 %}
我首先需要知道情況有多糟:
for i in 1 2 3 4 5; do /usr/bin/time zsh -i -c exit 2>&1; done
哎呀:
0.94 real
0.71 real
0.74 real
0.73 real
0.72 real
平均每次啟動 shell 耗時約 770 毫秒。光是打開一個終端就幾乎要花一秒鐘。這可不好。
克勞德·科德幫我找到了主要罪魁禍首:
| 工具 | 影響 | 為什麼它很糟糕 |
|------|--------|--------------|
| nvm | 耗時約 300-500 毫秒 | 每次都會載入整個 Node.js 環境 |
| pyenv 初始化 | 耗時約 100-200 毫秒 | Python 版本管理初始化 |
| 安全性指令 | 約 50-100 毫秒 | 從 macOS 鑰匙圈取得 API 金鑰 |
| brew shellenv | ~30-50毫秒 | 執行子shell以取得Homebrew路徑 |
| gcloud 程式碼補全 | 約 20-30 毫秒 | Google Cloud 程式碼補全 |
大多數工具都不需要提前加載,直到我實際使用它們時才加載。所以,把那些耗時耗力的軟體等到第一次執行指令時再載入。
這是巧妙之處。感謝 Claude Code。這些包裝函數在首次使用後會自動移除:
首次呼叫:包裝器執行,執行緩慢的初始化過程,然後刪除自身。
之後:直接執行,零開銷
每次療程只需支付一次費用。
而不是這段啟動緩慢的程式碼:
# Before: runs every shell startup (~400ms)
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"
nvm use default --silent
現在,我使用包裝函數:
# After: only runs when you actually need node/npm/npx
nvm() {
unset -f nvm node npm npx
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"
nvm "$@"
}
node() { nvm use default --silent; unfunction node npm npx; node "$@"; }
npm() { nvm use default --silent; unfunction node npm npx; npm "$@"; }
npx() { nvm use default --silent; unfunction node npm npx; npx "$@"; }
2025年11月28日更新:我已放棄使用nvm ,改用fnm ,因此也移除了nvm 、 node 、 npm和npx封裝函數。如果您確實需要使用nvm ,請保留這些封裝函數。
unset -f和unfunction指令會在首次使用後移除包裝器。之後,工具就像正常載入過一樣。
# Before
eval "$(pyenv init -)"
# After
pyenv() {
unset -f pyenv
eval "$(command pyenv init -)"
pyenv "$@"
}
第一次呼叫pyenv需要大約 150 毫秒進行初始化,之後就會一直直接執行。
gcloud() {
unset -f gcloud gsutil bq
[ -f "$HOME/.local/google-cloud-sdk/path.zsh.inc" ] && . "$HOME/.local/google-cloud-sdk/path.zsh.inc"
[ -f "$HOME/.local/google-cloud-sdk/completion.zsh.inc" ] && . "$HOME/.local/google-cloud-sdk/completion.zsh.inc"
gcloud "$@"
}
gsutil() { gcloud; gsutil "$@"; }
bq() { gcloud; bq "$@"; }
Homebrew 的環境不會經常變化,所以只需快取即可:
# Before: subshell every time
eval "$(/opt/homebrew/bin/brew shellenv)"
# After: cache to file, only regenerate if brew changes
if [[ ! -f ~/.zsh_brew_cache || ~/.zsh_brew_cache -ot /opt/homebrew/bin/brew ]]; then
/opt/homebrew/bin/brew shellenv > ~/.zsh_brew_cache
fi
source ~/.zsh_brew_cache
每次啟動 shell 時,我都會存取 macOS 鑰匙圈:
# Before: slow keychain lookup every time
export OPENAI_API_KEY=$(security find-generic-password -a $USER -s openai_api_key -w)
由於我只需要它用於 npm 相關操作,所以我把它移到了 npm 包裝器中:
npm() {
nvm use default --silent
unfunction node npm npx
[ -z "$OPENAI_API_KEY" ] && \
export OPENAI_API_KEY=$(security find-generic-password -a $USER -s openai_api_key -w)
npm "$@"
}
2025年11月28日更新:如上所述,我已切換到fnm ,因此我修改了程式碼,因為我移除了npm封裝函數。現在,對於我的OpenAI API金鑰,我只需在需要時呼叫一個函數即可:
# === Lazy-load OPENAI_API_KEY ===
openai_key() {
export OPENAI_API_KEY=$(security find-generic-password -a $USER -s openai_api_key -w)
}
我還清理了一些基本的東西:
將 7 種不同的 PATH 修改合併成一行
刪除重複的GPG_TTY匯出
修復了順序問題,現在STARSHIP_CONFIG會在starship init之前設定。
經歷所有變化之後:
for i in 1 2 3 4 5; do /usr/bin/time zsh -i -c exit 2>&1; done
0.06 real
0.04 real
0.04 real
0.03 real
0.04 real
平均值:約 40 毫秒
| 改善前 | 改善後 | 提升後 |
|--------|-------|-------------|
| 770毫秒 | 40毫秒 |速度提升95% |
是的,每種工具首次使用時都需要一次付費:
第一個node / npm / npx :+400毫秒
首次pyenv :+150毫秒
首次gcloud :+50毫秒
但這只會在每個終端會話中發生一次,而且與命令實際執行的操作相比,這幾乎難以察覺。
如果你的 shell 啟動速度很慢,先測量總啟動時間:
time zsh -i -c exit
如果超過 200 毫秒,表示還有提升空間。要查看具體是哪個環節出了問題,請分析你的 .zshrc 檔案或你正在使用的任何其他 shell:
# Add to top of .zshrc
zmodload zsh/zprof
# Add to bottom
zprof
這會詳細列出哪些特定命令佔用了您的啟動時間。
這是我更新的 shell,其中包含了所有效能最佳化。
# === Early exports (no subshells) ===
export HOMEBREW_NO_AUTO_UPDATE=1
export GOPATH=$HOME/go
export PYENV_ROOT="$HOME/.pyenv"
export POMERIUM_CLI_USER_DATA_DIRECTORY=$HOME/.local/share/pomerium
export STARSHIP_CONFIG=~/.config/starship.toml
export GPG_TTY=$(tty)
export HISTORY_IGNORE="(g\src|g\sra|g\sa|g\srhh|ls|cd|cd ..|pwd|clear|exit|logout|history|alias|unalias|set|unset|env|whoami|date|uptime|tree|code|code \.|vim|nvim|nano|trash|security)( .*)?"
export PATH="$HOME/.bun/bin:$HOME/.antigravity/antigravity/bin:$HOME/.config/herd-lite/bin:$HOME/.codeium/windsurf/bin:$HOME/.console-ninja/.bin:/opt/homebrew/anaconda3/bin:$GOPATH/bin:$PYENV_ROOT/bin:$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
openai_key() {
export OPENAI_API_KEY=$(security find-generic-password -a $USER -s openai_api_key -w)
}
# === Cache brew shellenv ===
if [[ ! -f ~/.zsh_brew_cache || ~/.zsh_brew_cache -ot /opt/homebrew/bin/brew ]]; then
/opt/homebrew/bin/brew shellenv > ~/.zsh_brew_cache
fi
source ~/.zsh_brew_cache
# === Zsh options ===
setopt autocd
autoload -U history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[[A" history-beginning-search-backward-end
bindkey "^[[B" history-beginning-search-forward-end
# === Prompt ===
eval "$(starship init zsh)"
# === fnm ===
eval "$(fnm env --use-on-cd --shell zsh)"
# === Lazy-load pyenv ===
pyenv() {
unset -f pyenv
eval "$(command pyenv init -)"
pyenv "$@"
}
# Lazy load Cargo - defers initialization until first use
cargo() {
unset -f cargo rustc rustup
source $HOME/.cargo/env
cargo "$@"
}
rustc() {
unset -f cargo rustc rustup
source $HOME/.cargo/env
rustc "$@"
}
rustup() {
unset -f cargo rustc rustup
source $HOME/.cargo/env
rustup "$@"
}
# === Plugins ===
source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
# === Atuin ===
. "$HOME/.atuin/bin/env"
eval "$(atuin init zsh --disable-up-arrow)"
# === Lazy-load gcloud ===
gcloud() {
unset -f gcloud gsutil bq
[ -f "$HOME/.local/google-cloud-sdk/path.zsh.inc" ] && . "$HOME/.local/google-cloud-sdk/path.zsh.inc"
[ -f "$HOME/.local/google-cloud-sdk/completion.zsh.inc" ] && . "$HOME/.local/google-cloud-sdk/completion.zsh.inc"
gcloud "$@"
}
gsutil() { gcloud; gsutil "$@"; }
bq() { gcloud; bq "$@"; }
# === Aliases ===
alias flushdns='sudo dscacheutil -flushcache;sudo killall -HUP mDNSResponder'
alias zshconfig='less ~/.zshrc'
alias nr='npm run'
alias ni='npm i'
alias '$'=''
alias brew='env PATH="${PATH//$(pyenv root)\/shims:/}" brew'
alias dotfiles='/opt/homebrew/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
alias g='git'
alias code='code-insiders'
alias c='cursor -r'
alias p='pnpm'
alias pi='pnpm i'
alias dcu='docker compose up -d'
alias dcd='docker compose down'
alias mermaid='mmdc'
alias sniffly='uvx sniffly init'
# === Functions ===
# checkout a pull request in a git worktree
cpr() {
pr="$1"
remote="${2:-origin}"
branch=$(gh pr view "$pr" --json headRefName -q .headRefName)
git fetch "$remote" "$branch"
git worktree add "../$branch" "$branch"
cd "../$branch" || return
echo "Switched to new worktree for PR #$pr: $branch"
}
rmmerged() {
git branch --merged | grep -v "*" | grep -v \"master\" | xargs -n 1 git branch -d && git remote prune origin
}
nb() {
branch="$1";
git remote -v | grep -q [email protected]:pomerium/ && git checkout -b "nickytonline/$branch" || git checkout -b $branch;
}
db() {
branch="$1";
git remote -v | grep -q [email protected]:pomerium && git branch -D "nickytonline/$branch" || git branch -D $branch;
}
glog() {
git log --oneline --decorate --graph --color | less -R
}
開發工具的開銷會迅速累積,很容易被這些細小的缺陷所掩蓋。這種延遲載入模式可以解決這個問題,而且沒有任何真正的缺點。
非常感謝 John L. 和 Claude Code 幫助我找出瓶頸和解決方案。
注意:效能影響資料和對比表格是在 Claude Code 的幫助下產生的。實際里程可能因您的具體配置而異。 😅
如果你想和我保持聯繫,我的所有社交帳號都在nickyt.online 。
期待下次!
照片由Marc Sendra Martorell拍攝,來自Unsplash
原文出處:https://dev.to/nickytonline/how-i-used-claude-code-to-speed-up-my-shell-startup-by-95-m0f