主要问题描述

        由于项目原因需要经常在windows下切换node环境,找了下FNM资料,没发现合适的文章。所以主要问题就是在不想切换linux环境,也不想用poweshell,还不使用cmder的情况下,每次打开cmd自动设置环境变量。

解决方案:

解决方法1:设置autorun.cmd

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d @^%USERPROFILE^%\autorun.cmd" "2^>NUL /f

  • 打开注册表编辑器(regedit)。

  • 导航到:HKEY_CURRENT_USER\Software\Microsoft\Command Processor

  • 如果没有Autorun键,右键新建一个String Value(字符串值),命名为Autorun

  • 设置值为C:\path\to\autorun.cmd(也可以替换为你实际的autorun.cmd路径)。

    You can set up a startup script and append the following line:

1
2
3
4
5
6
if defined FNM_ENV (
exit /b
)
set FNM_ENV=true
FOR /f "tokens=*" %%i IN ('fnm env --use-on-cd') DO CALL %%i
set FNM_ENV=

添加一个标记,用来判断是否已经执行过FOR /f "tokens=*" %%i IN ('fnm env --use-on-cd') DO CALL %%i,防止出现死循环。类似~/.bashrc,每个会话都执行一遍。

解决办法2:设置autorun.cmd

1
2
3
4
5
fnm env --use-on-cd > %USERPROFILE%/fnm.txt
for /f "delims=" %%i in (%USERPROFILE%/fnm.txt) do (
call %%i
)
del %USERPROFILE%/fnm.txt

曾经使用了一段时间的方法,缺点是每次都要创建删除文件。

手动执行测试:

1
FOR /f "tokens=*" %i IN ('fnm env --use-on-cd') DO CALL %i

错误示范:

1、首先尝试了在批处理里面添加:

FOR /f "tokens=*" %%i IN ('fnm env --use-on-cd') DO CALL %%i

测试结果:无限循环

原因分析:

autorun.cmd中使用FOR /f "tokens=*" %%i IN ('fnm env --use-on-cd') DO CALL %%i时,造成无限循环的原因可能与CALL命令如何执行命令有关。CALL命令会执行指定的命令,并且在完成后返回到当前批处理脚本继续执行。如果fnm env --use-on-cd的输出包含了某个命令,而这个命令在执行时又触发了对autorun.cmd的调用,就会导致无限循环。

2、尝试直接引用脚本:

1
2
3
4
5
6
7
:: %CMDER_ROOT%\bin\fnm_init.cmd
@echo off
FOR /f "tokens=*" %%z IN ('fnm env --use-on-cd') DO CALL %%z


:: %CMDER_ROOT%\config\user_profile.cmd
call "%CMDER_ROOT%\bin\fnm_init.cmd"

测试结果:无限循环

思维发散

那么问题来了,

1、如何判断cmd是第一次启动呢?

设置环境变量、设置标志文件、读取系统文件进行判断、开机脚本、关机脚本、计划任务……

有空再研究。

2、是否可以固定环境变量呢?

1
2
3
4
5
6
7
8
FNM_ARCH=x64
FNM_COREPACK_ENABLED=false
FNM_DIR=C:\Users\NAME\AppData\Roaming\fnm
FNM_LOGLEVEL=info
FNM_MULTISHELL_PATH=C:\Users\NAME\AppData\Local\fnm_multishells\29828_1735989106450
FNM_NODE_DIST_MIRROR=https://nodejs.org/dist
FNM_RESOLVE_ENGINES=true
FNM_VERSION_FILE_STRATEGY=local

应该是可以,假设修改FNM_DIR:

setx /M FNM_DIR "D:\Program Files\npm"

不过这样的话,autorun.cmd的脚本就要更改了,会存在冲突。个人理解,其实只需要设置FNM_MULTISHELL_PATH,其他都可以配置成永久环境变量。

FNM_MULTISHELL_PATH=C:\Users\NAME\AppData\Local\fnm_multishells`29828_1735989106450`这串数字每次都是变化的。

我没有测试,感兴趣的可以自行测试。

总结

        本来是个小问题,就是稍微研究研究。可以看到github上的issue,几乎有一半都是跟环境变量有关系(未经严格统计)。懒人症发作,其实不想写的,纠结写还是不写的时间,可能比写下来的时间要久。可见无论要做什么,真正行动起来是最重要的。

参考

https://github.com/Schniz/fnm

fnm/docs/commands.md at master · Schniz/fnm · GitHub

Environment variables need to be setup before you can start using fnm. This is done by evaluating the output of fnm env.

PowerShell

Add the following to the end of your profile file:

1
fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression
  • For macOS/Linux, the profile is located at ~/.config/powershell/Microsoft.PowerShell_profile.ps1

  • For Windows location is either:

    • %userprofile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 Powershell 5
    • %userprofile%\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 Powershell 6+
  • To create the profile file you can run this in PowerShell:

    1
    if (-not (Test-Path $profile)) { New-Item $profile -Force }
  • To edit your profile run this in PowerShell:

    1
    Invoke-Item $profile

Windows Command Prompt aka Batch aka WinCMD

fnm is also supported but is not entirely covered. You can set up a startup script for cmd.exe or Windows Terminal and append the following lines:

1
2
3
4
5
6
@echo off
:: for /F will launch a new instance of cmd so we create a guard to prevent an infnite loop
if not defined FNM_AUTORUN_GUARD (
set "FNM_AUTORUN_GUARD=AutorunGuard"
FOR /f "tokens=*" %%z IN ('fnm env --use-on-cd') DO CALL %%z
)

Usage with Cmder

Usage is very similar to the normal WinCMD install, apart for a few tweaks to allow being called from the cmder startup script. The example assumes that the CMDER_ROOT environment variable is set to the root directory of your Cmder installation. Then you can do something like this:

  • Make a .cmd file to invoke it
1
2
3
:: %CMDER_ROOT%\bin\fnm_init.cmd
@echo off
FOR /f "tokens=*" %%z IN ('fnm env --use-on-cd') DO CALL %%z
  • Add it to the startup script
1
2
:: %CMDER_ROOT%\config\user_profile.cmd
call "%CMDER_ROOT%\bin\fnm_init.cmd"

You can replace %CMDER_ROOT% with any other convenient path too.