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

配图源自 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?

五、参考文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容