Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

解决 Unable to resolve your shell environment in a reasonable time. #47

Open
toFrankie opened this issue Feb 25, 2023 · 0 comments
Open
Labels
2022 2022 年撰写 Editor 与 VS Code 等开发编辑器相关的文章 Mac 与 Mac、macOS 相关的文章 Terminal 与 Terminal 相关的文章

Comments

@toFrankie
Copy link
Owner

配图源自 Freepik

一、背景

不知道什么时候起,我那服役了 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 for nvm 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_timeuser_timesys_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 using nvm until you manually use 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:这个没了解过...
  • 更多请看...

关于 管理 node 版本,选择 nvm 还是 n?

五、参考文章

@toFrankie toFrankie added Mac 与 Mac、macOS 相关的文章 Editor 与 VS Code 等开发编辑器相关的文章 Terminal 与 Terminal 相关的文章 labels Feb 25, 2023
@toFrankie toFrankie added the 2022 2022 年撰写 label Apr 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2022 2022 年撰写 Editor 与 VS Code 等开发编辑器相关的文章 Mac 与 Mac、macOS 相关的文章 Terminal 与 Terminal 相关的文章
Projects
None yet
Development

No branches or pull requests

1 participant