WSL即插即用策略

最的一些工作需要涉及到很多 windows 独占的引擎和工具，所以暂时还离不开 windows 环境（没法摸鱼），但又有炼丹需求，所以打算试试 WSL，安装一个可移动的，windows 友好的 ubuntu 实例。

安装 WSL Ubuntu

直接去 Microsoft Store 获取 WSL Ubuntu 实例。

这里可能涉及到一些 Clash 用户无法打开 Store，只需要在 Clash 设置里设置 UWP Loopback，勾选 Store - Save 即可。

安装包可以在C:\Program Files\WindowsApps\CanonicalGroupLimited.Ubuntu22.04LTS_2204.5.10018.0_x64__79rhkp1fndgsc 找到，其大小恒定。

而真正的系统内容则存储在： %LocalAppData%\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc

因此有必要把他迁移到其他盘符。

提取安装包

以管理员方式打开 power shell， 安装 windows linux 子系统支持

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

安装完毕后， 按 Y 重启。

之后可以设置 wsl 的模式，

wsl --set-default-version 1

wsl1 和 wsl2 的区别在于，wsl2 做到了系统的彻底隔离，类似虚拟机，而 wsl1 是把 linux 的文件直接呈现在 NTFS 系统中。如果设置了 wsl 2，安装的时候可能会出现错误WslRegisterDistribution failed with error: 0x800701bc

这是因为没有对应 wsl2 的内核，需要 wsl 官网下载更新包

来到安装包位置，打包复制进移动硬盘（这里是 F:）

双击可执行程序安装。

在这里我遇到了一些问题，安装界面卡在 intalling ... 很久， github issue 提出了一个解决方案：I got the same issue, but found a workaround: you just have to use ctrl+c and then it will continue and ask you for a username and password

然后成功安装。 因为我们选择的是 wsl 1 模式，所以能够看到在 rootfs 文件夹下 linux 系统文件 F:\Ubuntu\rootfs

至此，可以再次打开刚才执行安装的可执行程序（Ubuntu2204）测试

清理内容

在开始菜单中卸载 Ubuntu， 用来释放 C 盘的安装包。

从移动硬盘分离主机
来到注册表位置 ：

计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss{*****}

最后一个位置是随机的GUID，打开可以看到两个关键内容：

BasePath : F:\Ubunut DistributionName: Ubuntu-22.04
path用来指示 wsl，linux 实例的目录（rootfs），name 用来告知这个实例的名字（wsl –list）

其余的几项分别是用来激活登录用户和设置 wsl 模式的值，总之，有了这个信息，可以使用命令行直接启动 wsl：

wsl -d Ubuntu-22.04

我们把这个注册表导出到 Ubuntu 目录:

Copy
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss{8f49723d-d252-404e-b36a-e376ee44c835}]
"State"=dword:00000001
"DistributionName"="Ubuntu-22.04"
"Version"=dword:00000002
"BasePath"="F:\Ubuntu"
"Flags"=dword:00000007
"DefaultUid"=dword:000003e8

当我们需要在另一个主机上打开这个实例时，只需要修改 basepath 到当前移动硬盘在另一个主机上的盘符，（F: -> E:），然后双击这个注册表，把注册表安装进另一个主机即可。

如果是多个 wsl 实例，则需要注意不要重名

通过一个批处理文件实现。

Copy
@echo off
setlocal EnableDelayedExpansion

set batfilenam=%~nx0
set batdir=%~dp0
set batdir=%batdir:~0,-1%
set batdir2=%batdir:=\%

set ThisWslDistributionName=%~1
set UnixUser=%~2

REM =========== BAT PARAMETER CHECKING ==============

if not defined ThisWslDistributionName (
echo. Need a DistributionName as parameter.
echo. The DistributionName is a new name to be registered into Lxss service.
echo. This .bat is compatible with Win10.21H2 or above.
echo. Examples:
echo. %batfilenam% Ubuntu-22.04
echo. %batfilenam% Ubuntu-22.04 bob
exit /b 4
)

if not "" == "%~3" (
call :Echos Error: Only one or two parameter should be given.
exit /b 4
)

call :HasSpaceChar %ThisWslDistributionName%
if %ERRORLEVEL%==1 (
call :Echos Error: DistributionName must NOT have space char in it.
exit /b 4
)

if defined UnixUser (
call :HasSpaceChar %UnixUser%
if %ERRORLEVEL%==1 (
call :Echos Error: UnixUser must NOT have space char in it.
exit /b 4
)
set _u_param=-u %UnixUser%
) else (
set _u_param=
)

REM =========== ENV CHECKING ==============

set wsl1_checkdir=%batdir%\rootfs\root
set wsl1_seen=
if exist "%wsl1_checkdir%" set wsl1_seen=1

set wsl2_checkfile=%batdir%\ext4.vhdx
set wsl2_seen=
if exist "%wsl2_checkfile%" set wsl2_seen=1

if "%wsl1_seen%%wsl2_seen%" == "" (
call :Echos Error: No WSL file-system found in "%batdir%" .
call :Echos You should either have
call :Echos "%wsl1_checkdir%" ^(for WSL1^)
call :Echos or
call :Echos "%wsl2_checkfile%" ^(for WSL2^)
exit /b 4
)

REM =========== PREPARE .REG FILE ==============

REM We need to generate a GUID for current DistributionName,
REM We can ensure it being unique by calcuating %ThisWslDistributionName%.
echo %ThisWslDistributionName%> "%batdir%\wslname.tmp"

call :GetFileMD5 "%batdir%\wslname.tmp" md5

if not defined md5 (
call :Echos Error: Cannot generate a GUID for ThisWslDistributionName.
exit /b 4
)

set md5guid=%md5:~0,8%-%md5:~8,4%-%md5:~12,4%-%md5:~16,4%-%md5:~20,12%
rem For "Ubuntu-22.04", md5guid is
rem 2b0c8e79-b4a5-1bfc-49cd-4f344cbfe444

set md5finalchar=%md5:~31,1%
if not defined md5finalchar (
call :Echos Error: When generating MD5 for ThisWslDistributionName, certttil.exe returns expected result.
call :Echos md5=%md5%
exit /b 4
)

set badoutput=%md5:~32,1%
if defined badoutput (
call :Echos Error: When generating MD5 for ThisWslDistributionName, certttil.exe returns expected result.
call :Echos md5=%md5%
exit /b 4
)

set regfile=%batdir%\WSL-green.reg

echo Windows Registry Editor Version 5.00 >> "%regfile%"
echo.>> "%regfile%"
echo [HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss{%md5guid%}]>> "%regfile%"

if defined wsl1_seen (
echo "Flags"=dword:00000007 >> "%regfile%"
set wslverhint=WSL1
) else if defined wsl2_seen (
echo "Flags"=dword:0000000f >> "%regfile%"
set wslverhint=WSL2
) else (
echo "wsl version not found" >> "%regfile%"
)

echo "BasePath"="%batdir2%" >> "%regfile%"
echo "DistributionName"="%ThisWslDistributionName%" >> "%regfile%"

type "%batdir%\WSL-green.reg.0" >> "%regfile%"

REM =========== IMPORTING REGISTRY ==============

call :Echos Registering WSL instance using:
call :Echos . WSL version = %wslverhint%
call :Echos . GUID = {%md5guid%}
call :Echos . DistributionName = %ThisWslDistributionName%

call :EchoAndExec reg import "%regfile%"
if not %ERRORLEVEL%==0 (
call :Echos Import registry file fail: %regfile%
pause
exit /b 4
)

title WSL - %ThisWslDistributionName%

call :EchoAndExec wsl -d %ThisWslDistributionName% %_u_param% --cd ~
if not %ERRORLEVEL%==0 (
echo.
echo "wsl -d" execution fail. Please check your bat file or Windows environment.
echo.
pause
exit /b 4
)

exit /b 0

REM =============================
REM ====== Functions Below ======
REM =============================

:Echos
REM This function preserves %ERRORLEVEL% for the caller,
REM and, LastError does NOT pollute the caller.
setlocal & set LastError=%ERRORLEVEL%
echo [%batfilenam%] %*
exit /b %LastError%

:EchoAndExec
echo [%batfilenam%] EXEC: %*
call %*
exit /b %ERRORLEVEL%

:HasSpaceChar
REM Check if the input params has space-char in it.
REM
REM call :HasSpaceChar param1 param2
REM call :HasSpaceChar "param1 param2"
REM -- ERRORLEVEL above will be 1, means yes.
REM
REM call :HasSpaceChar ans param1-param2
REM -- ERRORLEVEL above will be 0, means no.
REM
REM If no input param, consider it "have" spacechar.

setlocal & set input=%*
if not defined input exit /b 1

set input=%input:"=#%
set count=0
for %%i in (%input%) do (
set /A count=count+1
)
if %count% == 1 (
exit /b 0
) else (
exit /b 1
)

:GetFileMD5
REM Thanks to: https://github.com/npocmaka/batch.scripts/blob/master/fileUtils/md5.bat
REM Usage:
REM call :GetFileMD5
set "md5="
for /f "skip=1 tokens=* delims=" %%# in ('certutil -hashfile "%~f1" MD5') do (
if not defined md5 (
for %%Z in (%%#) do set "md5=!md5!%%Z"
)
)
endlocal && (set "%~2=%md5%")

后续只需要双击 bat 文件即可实现即插即用