解决 Unable to resolve your shell environment in a reasonable time.
一、背景
不知道什么时候起,我那服役了 5 年多的 MacBook Pro,每次重启后立刻唤醒 VS Code 的时候,总会弹出提示:
Unable to resolve your shell environment in a reasonable time. Please review your shell configuration.
无法在合理的时间内解析 shell 环境。请检查 shell 配置。
打开 VS Code 的方式有:
- 命令行启动
code path/to/file
- 点击应用图标
上述问题,仅在非命令行启动才会出现。也就是说,它就是解决问题的方式之一。但我想,更多人是通过点击 LaunchPad 或 Dock 栏应用图标启动的。
二、原因
复现步骤:只要在 Shell 配置文件中添加一行 sleep 30
(睡眠 30s,实际上超过 10s 即可),然后重启 VS Code 就能看到该提示。
从 Resolving shell environment fails 可知:
通过非命令行方式启用 VS Code 时,它会启动一个小进程来运行 Shell 环境,也就是执行
.bashrc
或.zshrc
配置文件。如果 10s 后,Shell 环境仍未解析完成或者由于其他原因导致解析失败,那么 VS Code 将会终止解析,然后就会提示:Unable to resolve your shell environment in a reasonable time. Please review your shell configuration.
由于使用命令行启动 VS Code,它会继承 Shell 环境变量,因此不会出现上述问题(#717)。
至于为什么 VS Code 在启动时要解析 Shell ?从其描述上看,大概是因为像 task、debug targets、plugins 等功能需要读取 Shell 环境变量。
因此,只要确保 Shell 配置不出错,且解析时间在 10s 之内,就能解决问题了。
官方给出的排查步骤如下:
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 启动耗时测试
本节以 zsh 为例。
首先,这里利用自带的 time
命令来衡量命令执行用时(包括 zsh)。
$ /usr/bin/time /bin/zsh -i -c exit 0.62 real 0.33 user 0.32 sys
time
命令结果输出由 real_time
、user_time
和 sys_time
组成:
real_time
:表示从程序开始到程序执行结束时所消耗的时间,包括 CPU 的用时和所有延迟程序执行的因素的总和。其中 CPU 用时被划分为 user 和 sys 两部分。user_time
:表示程序本身以及它所调用的库中的子进程使用的时间。sys_time
:表示由程序直接或间接调用的系统调用执行的时间。
但注意三者并没有严格的关系。通常单核 CPU 是 real_time > user_time + sys_time
,而多核 CPU 则是 real_time < user_time + sys_time
,更多请看。
以上 zsh 启动时间仅 0.62s,为了数据更准确,使用 for 循环连续启动 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
参数)看看有多快(以下显示为 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 各个函数的执行用时。在 ~/.zshrc
配置文件中添加一行以加载 zprof
模块。
# ~/.zshrc zmodload zsh/zprof
接着使用 zprof
命令获取各函数用时数据:
$ 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 耗时问题
当然,影响 zsh 启动用时的不仅仅有 nvm,具体因人而异。
我这里除了 Oh My Zsh 的一些东西(有空再收拾它)之外,就属 nvm 耗时最大了。
方案一(不推荐)
用 Google 搜索 Unable to resolve your shell environment in a reasonable time.
应该很容易找到类似以下的解决方法:
在 .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
思路很简单,利用环境变量 TERM_PROGRAM
判断调用 Shell 的应用程序,如果是 VS Code 的话,就不加载 nvm,以减少解析 Shell 的时间,从而解决文章开头的问题。
但从使用体验上看,有点傻,有点麻烦... 当你使用 VS Code 内置终端时,可以看到:
# 加载 .zshrc 的输出内容 in vscode, nvm not work; use `load-nvm` # 执行 nvm 命令出错,因为启动当前 Shell 时为加载 nvm,自然就找不到了 $ nvm current zsh: correct 'nvm' to 'nm' [nyae]? n zsh: command not found: nvm # 手动加载 nvm(前面声明的一个加载函数) $ load-nvm # 再次执行 nvm 命令 $ nvm current v16.14.0
对于一个长时间使用 VS Code 的用户来说,这是不能容忍的,即使使用 nvm 的次数也是寥寥无几。
方案二
在 nvm 文档中,可以发现:
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
参数,以推迟使用 nvm
。当你在使用时才会加载。修改 nvm 相关配置,如下:
# 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 多。最重要的是,它不会像方案一那样,还要手动执行加载 nvm 的命令。
方案三
听说 nvm 相比其他 Node 版本解决方案,要慢很多。可选的解决方案有:
- n:与 nvm 不同的是,它是一个 npm 包,也就是依赖于 node。而 nvm 是一个独立的程序。
- fnm:使用 rust 写的,是不是还没用就感觉到快了,哈哈。
- nvs:这个没了解过...
- 更多请看...
五、参考文章
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论