一、背景
不知道什么時候起淘捡,我那服役了 5 年多的 MacBook Pro球榆,每次重啟后立刻喚醒 VS Code 的時候充易,總會彈出提示:
Unable to resolve your shell environment in a reasonable time. Please review your shell configuration.
無法在合理的時間內(nèi)解析 shell 環(huán)境。請檢查 shell 配置裁着。
打開 VS Code 的方式有:
- 命令行啟動
code path/to/file
- 點擊應(yīng)用圖標(biāo)
上述問題繁涂,僅在非命令行啟動才會出現(xiàn)。也就是說二驰,它就是解決問題的方式之一扔罪。但我想,更多人是通過點擊 LaunchPad 或 Dock 欄應(yīng)用圖標(biāo)啟動的桶雀。
二矿酵、原因
復(fù)現(xiàn)步驟:只要在 Shell 配置文件中添加一行 sleep 30
(睡眠 30s唬复,實際上超過 10s 即可),然后重啟 VS Code 就能看到該提示全肮。
從 Resolving shell environment fails 可知:
通過非命令行方式啟用 VS Code 時敞咧,它會啟動一個小進程來運行 Shell 環(huán)境,也就是執(zhí)行
.bashrc
或.zshrc
配置文件辜腺。如果 10s 后休建,Shell 環(huán)境仍未解析完成或者由于其他原因?qū)е陆馕鍪。敲?VS Code 將會終止解析评疗,然后就會提示:Unable to resolve your shell environment in a reasonable time. Please review your shell configuration.
由于使用命令行啟動 VS Code测砂,它會繼承 Shell 環(huán)境變量,因此不會出現(xiàn)上述問題(#717)百匆。
至于為什么 VS Code 在啟動時要解析 Shell 砌些?從其描述上看,大概是因為像 task加匈、debug targets存璃、plugins 等功能需要讀取 Shell 環(huán)境變量。
因此矩动,只要確保 Shell 配置不出錯有巧,且解析時間在 10s 之內(nèi),就能解決問題了悲没。
官方給出的排查步驟如下:
The process outlined below may help you identify which parts of your shell initialization are taking the most time:
- Open your shell's startup file (for example, in VS Code by typing
~/.bashrc
or~/.zshrc
in Quick Open (?P)). - Selectively comment out potentially long running operations (such as
nvm
if you find that). - Save and fully restart VS Code.
- Continue commenting out operations until the error disappears.
Note: While
nvm
is a powerful and useful Node.js package manager, it can cause slow shell startup times, if being run during shell initialization. You might consider package manager alternatives such as asdf or search on the internet fornvm
performance suggestions.
把 Shell 配置文件中一些耗時操作給注釋掉以減小解析時間。這里 nvm 被點名了男图,沒錯示姿,我確實有用到它。
三逊笆、zsh 啟動耗時測試
本節(jié)以 zsh 為例栈戳。
首先,這里利用自帶的 time
命令來衡量命令執(zhí)行用時(包括 zsh)难裆。
$ /usr/bin/time /bin/zsh -i -c exit
0.62 real 0.33 user 0.32 sys
time
命令結(jié)果輸出由 real_time
子檀、user_time
和 sys_time
組成:
-
real_time
:表示從程序開始到程序執(zhí)行結(jié)束時所消耗的時間,包括 CPU 的用時和所有延遲程序執(zhí)行的因素的總和乃戈。其中 CPU 用時被劃分為 user 和 sys 兩部分褂痰。 -
user_time
:表示程序本身以及它所調(diào)用的庫中的子進程使用的時間。 -
sys_time
:表示由程序直接或間接調(diào)用的系統(tǒng)調(diào)用執(zhí)行的時間症虑。
但注意三者并沒有嚴(yán)格的關(guān)系缩歪。通常單核 CPU 是 real_time > user_time + sys_time
,而多核 CPU 則是 real_time < user_time + sys_time
谍憔,更多請看匪蝙。
以上 zsh 啟動時間僅 0.62s主籍,為了數(shù)據(jù)更準(zhǔn)確,使用 for 循環(huán)連續(xù)啟動 5 次:
$ for i in $(seq 1 5); do /usr/bin/time /bin/zsh -i -c exit; done
0.66 real 0.34 user 0.35 sys
0.64 real 0.34 user 0.34 sys
0.66 real 0.34 user 0.36 sys
0.66 real 0.34 user 0.36 sys
0.65 real 0.34 user 0.35 sys
如果不加載 ~/.zshrc
(使用 --no-rcs
參數(shù))看看有多快(以下顯示為 0 是因為太快了):
$ for i in $(seq 1 5); do /usr/bin/time /bin/zsh --no-rcs -i -c exit; done
0.00 real 0.00 user 0.00 sys
0.00 real 0.00 user 0.00 sys
0.00 real 0.00 user 0.00 sys
0.00 real 0.00 user 0.00 sys
0.00 real 0.00 user 0.00 sys
另外逛球,zsh 提供了專門的 profiling 模塊用于衡量 zsh 各個函數(shù)的執(zhí)行用時千元。在 ~/.zshrc
配置文件中添加一行以加載 zprof
模塊。
# ~/.zshrc
zmodload zsh/zprof
接著使用 zprof
命令獲取各函數(shù)用時數(shù)據(jù):
$ zprof
num calls time self name
-----------------------------------------------------------------------------------
1) 2 446.71 223.36 48.18% 190.57 95.28 20.55% compinit
2) 2 297.80 148.90 32.12% 170.88 85.44 18.43% nvm
3) 1 155.83 155.83 16.81% 155.83 155.83 16.81% compdump
4) 1 403.10 403.10 43.48% 105.29 105.29 11.36% nvm_auto
5) 1 112.56 112.56 12.14% 103.25 103.25 11.14% nvm_ensure_version_installed
6) 771 67.70 0.09 7.30% 67.70 0.09 7.30% compdef
7) 4 32.85 8.21 3.54% 32.85 8.21 3.54% compaudit
8) 1 30.02 30.02 3.24% 30.02 30.02 3.24% is_update_available
9) 2 42.88 21.44 4.63% 12.86 6.43 1.39% (anon)
10) 1 14.29 14.29 1.54% 10.26 10.26 1.11% nvm_die_on_prefix
11) 1 9.31 9.31 1.00% 9.31 9.31 1.00% nvm_is_version_installed
12) 192 7.64 0.04 0.82% 7.45 0.04 0.80% _zsh_autosuggest_bind_widget
13) 2 6.04 3.02 0.65% 6.04 3.02 0.65% update_terminalapp_cwd
14) 1 5.87 5.87 0.63% 5.87 5.87 0.63% nvm_supports_source_options
15) 1 13.21 13.21 1.43% 5.57 5.57 0.60% _zsh_autosuggest_bind_widgets
16) 1 4.52 4.52 0.49% 4.52 4.52 0.49% load_device_zsh_configuration
17) 1 3.83 3.83 0.41% 3.83 3.83 0.41% nvm_grep
18) 3 1.17 0.39 0.13% 1.17 0.39 0.13% up-line-or-beginning-search
19) 1 0.84 0.84 0.09% 0.84 0.84 0.09% colors
20) 5 1.75 0.35 0.19% 0.53 0.11 0.06% _zsh_autosuggest_invoke_original_widget
21) 4 2.11 0.53 0.23% 0.29 0.07 0.03% _zsh_autosuggest_widget_clear
22) 4 0.24 0.06 0.03% 0.24 0.06 0.03% add-zsh-hook
23) 1 0.23 0.23 0.02% 0.23 0.23 0.02% update_terminal_cwd
24) 4 4.03 1.01 0.43% 0.20 0.05 0.02% nvm_npmrc_bad_news_bears
25) 2 0.20 0.10 0.02% 0.20 0.10 0.02% is-at-least
26) 15 0.19 0.01 0.02% 0.19 0.01 0.02% _zsh_autosuggest_incr_bind_count
27) 3 1.98 0.66 0.21% 0.11 0.04 0.01% _zsh_autosuggest_bound_2_up-line-or-beginning-search
28) 5 0.10 0.02 0.01% 0.10 0.02 0.01% _zsh_autosuggest_highlight_reset
29) 5 0.09 0.02 0.01% 0.09 0.02 0.01% _zsh_autosuggest_highlight_apply
30) 4 1.68 0.42 0.18% 0.08 0.02 0.01% _zsh_autosuggest_clear
31) 1 0.31 0.31 0.03% 0.07 0.07 0.01% _zsh_autosuggest_widget_accept
32) 1 0.06 0.06 0.01% 0.06 0.06 0.01% nvm_has
33) 1 13.27 13.27 1.43% 0.06 0.06 0.01% _zsh_autosuggest_start
34) 2 0.06 0.03 0.01% 0.06 0.03 0.01% bashcompinit
35) 1 409.03 409.03 44.12% 0.06 0.06 0.01% nvm_process_parameters
36) 1 0.12 0.12 0.01% 0.06 0.06 0.01% complete
37) 3 0.05 0.02 0.01% 0.05 0.02 0.01% is_theme
38) 1 0.20 0.20 0.02% 0.05 0.05 0.00% _zsh_autosuggest_accept
39) 1 0.35 0.35 0.04% 0.04 0.04 0.00% _zsh_autosuggest_bound_1_forward-char
40) 1 0.04 0.04 0.00% 0.04 0.04 0.00% _zsh_autosuggest_orig_forward-char
41) 1 0.27 0.27 0.03% 0.03 0.03 0.00% _zsh_autosuggest_bound_1_accept-line
42) 1 0.03 0.03 0.00% 0.03 0.03 0.00% is_plugin
43) 1 0.03 0.03 0.00% 0.03 0.03 0.00% omz_termsupport_precmd
44) 1 0.02 0.02 0.00% 0.02 0.02 0.00% zle-line-finish
45) 1 0.02 0.02 0.00% 0.02 0.02 0.00% _zsh_autosuggest_orig_accept-line
46) 1 0.02 0.02 0.00% 0.02 0.02 0.00% detect-clipboard
47) 2 0.02 0.01 0.00% 0.02 0.01 0.00% env_default
48) 1 0.02 0.02 0.00% 0.02 0.02 0.00% omz_termsupport_preexec
49) 1 0.02 0.02 0.00% 0.02 0.02 0.00% zle-line-init
50) 1 0.01 0.01 0.00% 0.01 0.01 0.00% nvm_is_zsh
-----------------------------------------------------------------------------------
...
從這里可以看出 nvm
用時占比還是很大的颤绕。此前我在 Oh My Zsh 的 plugins
加載了一遍 nvm 插件诅炉,加上原有的 nvm 加載配置,啟動耗時來到 1.6s 左右屋厘,就很離譜涕烧,是我用錯了。
四汗洒、解決 nvm 耗時問題
當(dāng)然议纯,影響 zsh 啟動用時的不僅僅有 nvm,具體因人而異溢谤。
我這里除了 Oh My Zsh 的一些東西(有空再收拾它)之外瞻凤,就屬 nvm 耗時最大了。
方案一(不推薦)
用 Google 搜索 Unable to resolve your shell environment in a reasonable time.
應(yīng)該很容易找到類似以下的解決方法:
在 .zshrc
中添加以下配置:
function load-nvm {
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
}
# nvm
if [[ "x${TERM_PROGRAM}" = "xvscode" ]]; then
echo 'in vscode, nvm not work; use `load-nvm`'
else
load-nvm
fi
思路很簡單世杀,利用環(huán)境變量 TERM_PROGRAM
判斷調(diào)用 Shell 的應(yīng)用程序阀参,如果是 VS Code 的話,就不加載 nvm瞻坝,以減少解析 Shell 的時間蛛壳,從而解決文章開頭的問題。
但從使用體驗上看所刀,有點傻衙荐,有點麻煩... 當(dāng)你使用 VS Code 內(nèi)置終端時,可以看到:
# 加載 .zshrc 的輸出內(nèi)容
in vscode, nvm not work; use `load-nvm`
# 執(zhí)行 nvm 命令出錯浮创,因為啟動當(dāng)前 Shell 時為加載 nvm忧吟,自然就找不到了
$ nvm current
zsh: correct 'nvm' to 'nm' [nyae]? n
zsh: command not found: nvm
# 手動加載 nvm(前面聲明的一個加載函數(shù))
$ load-nvm
# 再次執(zhí)行 nvm 命令
$ nvm current
v16.14.0
對于一個長時間使用 VS Code 的用戶來說,這是不能容忍的斩披,即使使用 nvm 的次數(shù)也是寥寥無幾溜族。
方案二
在 nvm 文檔中,可以發(fā)現(xiàn):
You can add
--no-use
to the end of the above script (...nvm.sh --no-use
) to postpone usingnvm
until you manuallyuse
it.(詳見)
也就是添加 --no-use
參數(shù)垦沉,以推遲使用 nvm
煌抒。當(dāng)你在使用時才會加載。修改 nvm 相關(guān)配置乡话,如下:
# NVM
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
然后對比下摧玫,--no-use
添加前后 zsh 的啟動用時:
$ for i in $(seq 1 5); do /usr/bin/time /bin/zsh -i -c exit; done
0.23 real 0.16 user 0.07 sys
0.23 real 0.16 user 0.07 sys
0.23 real 0.16 user 0.07 sys
0.23 real 0.16 user 0.07 sys
0.23 real 0.16 user 0.07 sys
從之前的 0.6s 多降低到 0.2s 多。最重要的是,它不會像方案一那樣诬像,還要手動執(zhí)行加載 nvm 的命令屋群。
方案三
聽說 nvm 相比其他 Node 版本解決方案,要慢很多坏挠∩瞩铮可選的解決方案有:
- n:與 nvm 不同的是,它是一個 npm 包降狠,也就是依賴于 node对竣。而 nvm 是一個獨立的程序。
- fnm:使用 rust 寫的榜配,是不是還沒用就感覺到快了否纬,哈哈。
- nvs:這個沒了解過...
- 更多請看...
關(guān)于 管理 node 版本蛋褥,選擇 nvm 還是 n临燃?