
在 Windows 上装了 WSL 之后我养成了一个习惯能丢给 Linux 跑的脚本绝不在 Windows 上硬折腾。redis 没有官方 Windows 版我用 WSL 起elasticsearch 在 Windows 下各种权限和目录坑我也丢进 WSLbinwalk 这东西不装进 Linux 工具链基本没法愉快分析固件还有日常启停 jar 包的 manage.sh、backup.sh全都是在 WSL 里敲命令。但问题也跟着来了每次要跑一个 SH 脚本我得先打开终端敲wsl回车再 cd 到/mnt/c/...那一长串路径然后bash xxx.sh。路径一旦深一点或者目录名带中文那个输入体验真的让人崩溃。后来我花了一点时间做了一个很土但非常实用的工具把 .sh 文件直接从 Windows 资源管理器拖到一个批处理图标上它会自动把 Windows 路径转成 WSL 能识别的/mnt/c/...路径然后调用 WSL 里的 bash 执行这个脚本。整个过程不需要打开终端、不需要手敲路径、不需要记发行版名称。这篇文章就把这套方案的原理、完整脚本、踩坑记录都摊开讲清楚Windows 上用 WSL 跑 SH 脚本的朋友可以直接抄作业。1. 方案设计三个痛点逼出来的“拖拽执行”1.1 为什么我不再手动敲 WSL 命令先说说我日常工作里最常见的几个场景你应该也有同感。第一个是启动服务。比如我需要在 WSL 里启动 rediswsl进去之后redis 的启动脚本放在/etc/init.d/redis-server或者/opt/redis/start.sh每次都要先切到对应目录再执行。第二个是分析类脚本我把解包好的固件放在D:\firmware\extract下面想把 binwalk 跑一遍就得记着cd /mnt/d/firmware/extract再执行一串分析参数。第三个是项目部署脚本比如deploy.sh要更新 jar 包、重启服务这类脚本里面全是相对路径和当前目录判断如果你的工作目录不在它期望的位置脚本直接罢工。这些场景共同的特点是脚本本身已经写好了问题出在“怎么把它跑起来”。手动敲命令不光是慢还容易出低级错误。最常见的错误就是在wsl窗口里输入D:\xxx或者C:/xxx这种路径Linux shell 根本不认识反斜杠和盘符直接报No such file or directory。而且每次都要重复从 Windows 路径到 Linux 路径的脑内换算烦也烦死了。后来我就想Windows 用户本来就有一种非常自然的交互方式把一个文件拖到某个程序图标上让这个程序来处理它。我为什么不做一个接收器让 .sh 文件拖上去之后自动完成“路径转换 调用 WSL 执行脚本”这一整套动作事实证明这个方向完全正确用了几周之后我基本再也没手动敲过wsl命令来跑脚本。1.2 三条路线怎么选bat 接收器 / 右键菜单 / 发送到在动手之前我整理了三条可行的方案。这里先给你一张对比表后面会详细展开。方案实现难度操作体验是否需要管理员适用场景拖到专用 .bat 上执行低需要先拖拽然后等待运行结果不需要最通用适合所有 .sh 文件右键菜单“在 WSL 中运行”中右键点一下就行最顺手不需要改 HKCU 即可适合高频使用的 .sh 文件发送到菜单低右键 - 发送到 - 选择工具不需要不想改注册表的折中选择我的建议是先做方案一也就是拖到 bat 上执行。原因很简单bat 接收器是整个方案的逻辑核心右键菜单和发送到菜单最后都只是“调用 bat 的入口”而已。你把 bat 写对、调通后面两个方案基本就是复制粘贴的事情。方案二是注册表右键菜单修改的是HKEY_CURRENT_USER\Software\Classes下面的用户级文件关联不会动系统级配置所以不需要管理员权限。但它有一个副作用如果要让右键菜单稳定出现通常要把 .sh 文件的默认关联改成我们定义的 ProgID这样的话双击 .sh 文件也会变成“直接运行”而不是用编辑器打开。这个事情后面我会专门说明怎么权衡。方案三是发送到菜单把 bat 的一个快捷方式放进shell:sendto目录然后右键任意 .sh 文件通过“发送到”来执行。这个方案不改任何文件关联最安全也最适合“偶尔用一下”的人。2. 原理拆解WSL 和 Windows 之间到底怎么“传话”2.1 wsl.exe 命令行接口想要让 Windows 这边控制 WSL 干活核心就是wsl.exe这个命令行程序。它最基础的用法是直接敲wsl进去一个交互式 shell但对我们来说更重要的是非交互式执行命令。基本格式是wsl.exe [选项] [命令行]常用的几个选项-d 发行版或--distribution 发行版指定使用哪个 WSL 发行版比如-d Ubuntu-24.04。如果不指定就用默认发行版。-u 用户名或--user 用户名以指定用户身份执行不指定的话默认是发行版的默认用户。-e 命令或--exec 命令不通过 shell 直接执行命令。注意这个选项和直接拼接命令行的行为不太一样。我们最常写的形式是wsl.exe -d Ubuntu-24.04 bash -lc 你要执行的命令这里bash -lc的意思是启动一个登录 shell 并执行后面的命令。-l会加载用户的环境变量和 profile-c表示“由字符串指定命令”。很多脚本依赖PATH、JAVA_HOME这类环境变量所以-lc比不加-l更接近你手动打开 WSL 窗口再跑命令的效果。比如我想验证 WSL 里能不能拿到 Linux 路径直接执行wsl.exe -d Ubuntu-24.04 bash -lc echo hello from wsl如果配置没问题你会在当前 Windows 终端里看到hello from wsl。只要这一句通了后面所有事情就都通了。这里有一个非常关键的点wsl.exe是 Windows 这边的原生命令它启动 WSL 虚拟机时需要一点时间所以第一次调用会比较慢大概一到两秒。这很正常后面我们也会针对这一点做一些体验上的优化。2.2 路径转换C:\ 变 /mnt/c/ 的来龙去脉WSL 里的 Linux 子系统访问 Windows 磁盘并不是像 Windows 程序一样直接读C:\而是把 Windows 的盘符挂载到了 Linux 文件系统的/mnt/目录下。这是 WSL 的一个内置挂载规则C:\对应的就是/mnt/c/D:\对应的就是/mnt/d/以此类推。所以同样一个文件在 Windows 资源管理器里显示的是D:\firmware\extract\fw.bin但在 WSL 的 bash 里它的路径是/mnt/d/firmware/extract/fw.bin这两个路径的关系看起来很直接但让用户每次手动转换是很痛苦的。好在批处理可以做一个很简单的字符串替换。Windows 路径长这样C:\Users\admin\test.sh我们要做的就是两件事把所有反斜杠\替换成正斜杠/得到C:/Users/admin/test.sh。把开头的盘符C:改写成/mnt/c也就是在盘符前面加/mnt/并去掉冒号。从一个真实路径看会更清楚Windows 路径替换反斜杠后WSL 路径C:\Users\admin\test.shC:/Users/admin/test.sh/mnt/c/Users/admin/test.shD:\firmware\extract\run.shD:/firmware/extract/run.sh/mnt/d/firmware/extract/run.shE:\工具脚本\deploy.shE:/工具脚本/deploy.sh/mnt/e/工具脚本/deploy.sh批处理里做这个转换核心命令就是set的替换功能。%winpath:\/%会把变量winpath里的所有\替换成/然后我们再截取盘符和剩余路径拼起来。这段代码看起来有点绕但其实逻辑非常简单后面会给完整脚本。当然WSL 本身也提供了一个更权威的转换命令wslpath可以在 WSL 里把 Windows 路径转成 Linux 路径wsl.exe wslpath -u C:\Users\admin\test.sh输出结果是/mnt/c/Users/admin/test.sh。不过我在工具里并没有优先用wslpath原因有两个一是它会额外多启动一次 WSL 进程第一次调用会更慢二是批处理里做纯字符串替换不依赖 WSL 是否就绪逻辑更稳定。后面遇到特别复杂的路径再临时用wslpath兜底就行。2.3 拖到 bat 上之后参数经历了什么Windows 的批处理文件有一个特性当你把一个或多个文件拖到.bat文件图标上松开鼠标资源管理器会把文件的完整路径作为参数传给这个批处理。这些参数在批处理里可以通过%1、%2这样的位置变量来取%*表示所有参数的原始字符串。关键在于带空格的路径。比如你拖了一个D:\我的脚本\deploy test.sh资源管理器传给批处理时实际上会把路径用引号包起来所以%1的值是D:\我的脚本\deploy test.sh注意%1本身就带着引号。如果你直接拿%1去拼接路径就会出现引号嵌套的问题。批处理里有一个专门去掉引号的语法%~1它的意思是“取%1的值并去掉首尾的引号”。所以set winpath%~1这一句是用变量winpath保存去引号后的 Windows 路径后面所有的字符串替换都基于这个变量来做。为什么用set 变量值而不是直接set 变量值因为我担心路径里可能出现特殊字符用引号包裹整个赋值语句可以避免、%这类符号被 cmd 当成命令分隔符或变量引用处理。这是一个非常重要的防御性写法。另外拖拽多个文件到 bat 上时%*会包含所有路径每个路径还是带引号的。这时候可以用for循环逐个处理。这个后续在多文件升级版里会说。只要理解了这三件事wsl.exe怎么调用、Windows 路径怎么转成 WSL 路径、拖拽后的参数怎么正确处理整个工具的核心技术点就通了。剩下的就是把它们组装起来。3. 动手实现一套能直接复制的“拖拽运行”方案3.1 环境检查与测试脚本准备在写脚本之前先确认一下你的 WSL 环境是不是正常。打开任意终端执行wsl -l -v这个命令会列出当前系统里安装的 WSL 发行版和版本号。比如我的输出是NAME STATE VERSION * Ubuntu-24.04 Running 2注意 VERSION 这一列最好是 2。WSL2 的性能和兼容性比 WSL1 好很多尤其涉及到文件读写和大多数 Linux 工具链的时候。如果发现自己的发行版是 1可以考虑转换wsl --set-version Ubuntu-24.04 2如果还没有安装任何发行版用这一条命令装一个 Ubuntu 24.04 就行wsl --install -d Ubuntu-24.04安装过程会要求你设置 Linux 用户名和密码这个账号就是后面执行脚本时默认使用的用户。然后我们创建一个测试脚本。在 Windows 任意目录比如D:\wsl-test\下面新建一个hello.sh内容随便写点东西#!/bin/bash echo Hello from WSL echo Current path: $(pwd)先打开 WSL 窗口手动执行一下bash /mnt/d/wsl-test/hello.sh能正常输出说明文件系统挂载和执行权限都没问题。这里要注意一件事如果这个文件是从 Windows 记事本创建的大概率是 CRLF 换行符也就是每一行结尾既有回车符\r又有换行符\n而 Linux 只认\n。如果不做处理执行时会报类似$\r: command not found的错。这个坑我们后面专门讲现在先记住有这个风险。3.2 核心脚本 run-with-wsl.bat单文件版核心工具就是一个批处理文件名字随意我叫它run-with-wsl.bat。完整代码放在这里你可以直接复制保存。echo off chcp 65001 nul setlocal enabledelayedexpansion rem Configure your WSL distro here, leave empty to use the default set WSL_DISTROUbuntu-24.04 if %~1 ( echo Drag a .sh file onto this batch file. pause exit /b 1 ) set winpath%~1 echo Input: %winpath% set linuxpath%winpath:\/% if /i !linuxpath:~0,2!C: ( set drive!linuxpath:~0,1! set linuxpath/mnt/!drive!/!linuxpath:~2! ) else ( echo Unsupported path. Only drive-letter paths are supported. pause exit /b 1 ) echo WSL path: !linuxpath! rem Convert CRLF to LF first, then run the script if defined WSL_DISTRO ( wsl.exe -d %WSL_DISTRO% bash -lc sed -i s/\r$// !linuxpath! bash !linuxpath! ) else ( wsl.exe bash -lc sed -i s/\r$// !linuxpath! bash !linuxpath! ) echo. echo Script finished. pause下面逐段解释这个脚本做了什么。第一行echo off是关闭命令回显不然每执行一句命令都会刷一大片出来。第二行chcp 65001 nul把控制台代码页切到 UTF-8这样 WSL 里输出中文或者特殊符号时不容易乱码。第三行setlocal enabledelayedexpansion开启延迟变量展开因为我们后面会在括号块里动态改变和读取变量的值。WSL_DISTRO这一行是唯一的配置项。如果你只有一套发行版而且不想关心名字直接改成set WSL_DISTRO留空即可。留空之后脚本就会走 else 分支直接调用 WSL 默认发行版。如果你想指定发行版就用wsl -l -q查到准确名字填进去。然后判断%~1是否为空如果没有拖文件提示用户并暂停退出。这里pause很重要否则窗口会一闪而过你都看不清发生了什么。路径转换的核心代码是这三行set linuxpath%winpath:\/% if /i !linuxpath:~0,2!C: ( set drive!linuxpath:~0,1! set linuxpath/mnt/!drive!/!linuxpath:~2! )第一行把反斜杠全部变成正斜杠。第二行判断前两个字符是不是C:这里C:只是一个示例判断实际上它可以覆盖所有单字母盘符因为我用if /i做了大小写不敏感判断。如果是C:就取第一个字符作为盘符然后从第三个字符开始截取剩余路径拼成/mnt/C/...的格式。这里有个细节/mnt/C里面盘符是大写。WSL 的 DrvFs 挂载默认对盘符大小写不敏感所以/mnt/C和/mnt/c都能访问实际测试没有问题。如果你有强迫症非要用小写可以在批处理里用小写映射表但我觉得没必要。接下来就是真正执行命令。这里我做了一个我认为非常实用的处理在运行脚本之前先自动把 CRLF 换行符转成 LF。wsl.exe -d %WSL_DISTRO% bash -lc sed -i s/\r$// !linuxpath! bash !linuxpath!sed -i s/\r$//是原地删除每行结尾的\r这样不管你的脚本是在 Windows 记事本里写的还是从别处拷贝出来的到了 WSL 这边都能正常执行。表示前一个命令成功后才继续执行后面的bash。这个处理的代价是会原样修改你的 .sh 文件让它的换行符彻底变成 LF。如果你不希望改动原始文件可以把命令换成wsl.exe -d %WSL_DISTRO% bash -lc bash (tr -d \r !linuxpath!)这个写法用进程替换把去除\r后的内容直接喂给 bash不落盘、不改文件。但它有个副作用脚本里如果用了相对路径访问同目录下的其他文件当前工作目录可能对不上脚本行为会不一样。所以我的个人习惯是直接sed -i一劳永逸。把脚本保存好之后直接把D:\wsl-test\hello.sh拖到run-with-wsl.bat图标上你会看到弹出一个 cmd 窗口输出类似于Input: D:\wsl-test\hello.sh WSL path: /mnt/d/wsl-test/hello.sh Hello from WSL Current path: /mnt/d/wsl-test Script finished.注意pwd输出的是/mnt/d/wsl-test这说明 WSL 自动把当前工作目录映射到了你 Windows 文件所在的目录这个行为对脚本里使用相对路径特别友好。保存 bat 文件时要注意编码。如果你在 bat 里写了中文提示我建议把整个文件保存成 ANSI 编码或者干脆像我上面给出的脚本一样提示信息全用英文。因为chcp 65001只影响控制台输出对 cmd 解析批处理文件本身的作用并不完全可靠混合编码很容易出现乱码或者奇怪的报错。3.3 多文件拖拽的升级版单文件版本够用但有时候我们需要一次性跑多个脚本。比如我拿到一批固件样本每个样本有一个独立的analyze.sh我希望能一次把三个文件都拖到 bat 上逐个执行。多文件版本用for循环遍历参数即可。代码升级如下echo off chcp 65001 nul setlocal enabledelayedexpansion set WSL_DISTROUbuntu-24.04 if %~1 ( echo Drag one or more .sh files onto this batch file. pause exit /b 1 ) for %%F in (%*) do ( call :run_one %%~F ) echo. echo All scripts finished. pause exit /b 0 :run_one set winpath%~1 set linuxpath%winpath:\/% if /i !linuxpath:~0,2!C: ( set drive!linuxpath:~0,1! set linuxpath/mnt/!drive!/!linuxpath:~2! ) else ( echo Unsupported path: !winpath! exit /b 0 ) echo. echo Running: !linuxpath! if defined WSL_DISTRO ( wsl.exe -d %WSL_DISTRO% bash -lc sed -i s/\r$// !linuxpath! bash !linuxpath! ) else ( wsl.exe bash -lc sed -i s/\r$// !linuxpath! bash !linuxpath! ) exit /b 0核心变化是把执行逻辑封装成一个子过程:run_one主流程用for %%F in (%*)遍历所有拖进来的文件逐个调用。注意call :run_one %%~F这里先去掉引号再由call把参数安全地传进子过程这样即使在子过程里重新加引号也不会出现嵌套问题。多文件场景下有一个值得注意的行为每个脚本的执行是串行的前一个跑完才跑下一个。这符合大多数人的预期。如果你的脚本本身需要交互输入建议还是单个文件拖拽否则多个脚本交互会混在一起。3.4 进阶给 .sh 文件加“在 WSL 中运行”右键菜单拖拽方案虽然好用但有些人更喜欢右键菜单。尤其当你每天都要跑同一个位置的若干脚本时右键点一下肯定比拖拽更快。我采用的方式是修改用户级文件关联注册表让.sh文件关联到一个自定义 ProgID然后在这个 ProgID 下面注册右键动作。因为用的是HKEY_CURRENT_USER\Software\Classes所以不需要管理员权限。把下面内容保存成一个.reg文件双击导入。注意把命令里的路径改成你放置run-with-wsl.bat的实际路径。Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Software\Classes\.sh] WSLScript [HKEY_CURRENT_USER\Software\Classes\.sh\OpenWithProgids] WSLScript [HKEY_CURRENT_USER\Software\Classes\WSLScript] WSL SH Script [HKEY_CURRENT_USER\Software\Classes\WSLScript\DefaultIcon] %SystemRoot%\\System32\\wsl.exe,0 [HKEY_CURRENT_USER\Software\Classes\WSLScript\shell\open\command] \C:\\tools\\run-with-wsl.bat\ \%1\ [HKEY_CURRENT_USER\Software\Classes\WSLScript\shell\runwsl] Run with WSL [HKEY_CURRENT_USER\Software\Classes\WSLScript\shell\runwsl\command] \C:\\tools\\run-with-wsl.bat\ \%1\这个注册表文件做了几件事把.sh扩展名的默认 ProgID 设置成WSLScript。从这一刻起双击任何 .sh 文件Windows 就会去查WSLScript这个 ProgID 下的open命令也就是执行run-with-wsl.bat。定义了DefaultIcon让 .sh 文件在资源管理器里显示成 wsl.exe 的图标从视觉上提醒你这是“会用 WSL 执行的脚本”。定义了runwsl这个 shell 动词右键菜单里会出现“Run with WSL”选项效果和双击一样。导入之后如果发现右键菜单没有立刻生效可以注销再登录或者重启 explorer.exetaskkill /f /im explorer.exe start explorer.exe这里有一个非常重要的权衡必须说清楚修改默认关联之后双击 .sh 文件将不再用 VSCode、记事本之类的编辑器打开而是直接执行。如果你只是希望偶尔用 WSL 跑一下更推荐用“发送到”方案因为那不会改变默认关联。如果你确实需要保留编辑器打开能力可以把open命令指向编辑器比如 VSCode然后新增一个runwsl动词作为右键菜单项。具体做法是把上面.reg文件里的...\shell\open\command改成[HKEY_CURRENT_USER\Software\Classes\WSLScript\shell\open\command] \C:\\Users\\你的用户名\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe\ \%1\但这样双击又会变成编辑器打开和“直接运行”正好相反。反正鱼和熊掌不可兼得大家根据自己的习惯选择就行。3.5 最简单的部署把工具放进“发送到”菜单如果你只是想试用一下又不想改任何注册表发送到菜单是最友好的方式。按Win R输入shell:sendto回车后会打开“发送到”文件夹。把run-with-wsl.bat文件复制进去或者创建一个快捷方式放进去名字改成“运行到 WSL”之类的直观名称。之后选中任意一个 .sh 文件右键选择“发送到 - 运行到 WSL”批处理就会收到文件路径并执行。这个方案的好处是完全不干扰现有的文件关联对 .sh 文件的默认打开方式没有任何影响。坏处是右键菜单多了一层“发送到”操作路径比直接右键运行长一步。但作为入门体验它已经足够好了。4. 实操中的坑与排查技巧4.1 CRLF 换行符最常见的第一个坑我刚做这个工具的时候第一次拖一个在 Windows 记事本里编写脚本运行WSL 那边直接给我来了个莫名奇妙的错误$\r: command not found这个错误非常典型。Windows 记事本默认使用 CRLF 作为换行符也就是每一行结尾是\r\n两个字符。Linux 的 bash 只认\n当你把 CRLF 文件拿给 bash 时每一行末尾多出的那个\r就会被当成命令的一部分。最常见的结果就是最后一行的命令后面莫名其妙跟着一个\rbash 找不到叫这个名字的命令于是报错。解决办法有三个第一种在 WSL 里安装 dos2unix然后手动转换dos2unix /mnt/d/wsl-test/hello.sh第二种直接用 sed 原地转换这也是我在工具里采用的方式sed -i s/\r$// /mnt/d/wsl-test/hello.sh第三种使用tr但不修改原文件bash (tr -d \r /mnt/d/wsl-test/hello.sh)我的建议是让 bat 自动执行第二种。因为脚本本身是给 Linux 用的保持 LF 换行才是标准状态。第一次运行之后文件就彻底正常了以后再拖也不会再触发这个问题。4.2 路径里有空格、中文和特殊符号Windows 路径里带空格非常常见比如D:\My Scripts\deploy test.sh。只要你的批处理正确地使用了%~1空格不是问题。资源管理器传参数时已经给路径加上了引号%~1能正确取到完整路径。中文路径也是可以的。Windows 路径中的中文字符在传给 WSL 之后只要 WSL 的 locale 是 UTF-8绝大多数情况下都能正常处理。保险起见建议在 WSL 里执行locale看输出如果是C.UTF-8或者en_US.UTF-8中文路径基本没问题。如果出现乱码修改 WSL 的/etc/default/locale确保是 UTF-8 即可。真正麻烦的是和%这类字符。在 cmd 里是命令连接符如果你直接写set var%1当路径里有时后面的内容会被当成新的命令执行非常危险。这也是我在脚本里一直坚持set var%~1的原因引号把整个赋值语句的保护起来就只是路径里的一个普通字符。%在 cmd 里是变量引用符号出现在路径里也会被展开处理起来更麻烦一些。我的建议是如果路径里出现了%尽量不要走 bat 方案直接手动改一下文件名省得和自己较劲。4.3 wsl.exe 启动慢、找不到发行版第一次拖拽执行的时候你会明显感觉窗口卡了大概一两秒才出结果这是 WSL 冷启动的正常现象。如果你的 WSL 发行版刚好处于停止状态wsl.exe 会先启动子系统再执行命令耗时自然会长一些。如果经常要高频调用可以在 Windows 开机时让 WSL 保持运行或者接受这个延迟。多数情况下一两秒的延迟对跑脚本来说完全可接受。如果执行时遇到There is no distribution installed.说明你的 WSL 里还没有任何发行版先执行wsl --install -d Ubuntu-24.04如果是The specified distribution is not valid.说明WSL_DISTRO配置错了。用wsl -l -q查看准确的发行版名字注意大小写。比如Ubuntu-24.04中间是短横线Ubuntu和Ubuntu-24.04是两个不同的发行版别混了。还有一个小概率问题Windows 提示 “WSL needs updating”。这是因为系统的 WSL 内核太老。执行wsl --update更新到最新版即可。如果更新速度很慢可以考虑检查 Windows 更新和网络但不影响这个脚本本身的逻辑。4.4 窗口闪退和输出乱码如果你拖拽后窗口一闪而过什么都没看到最常见的原因是批处理最后没有pause。这个工具里我在正常结束和异常分支都加了pause所以理论上不会闪退。如果还是闪退你可以先打开一个 cmd 窗口手动执行一下run-with-wsl.bat D:\wsl-test\hello.sh这样窗口不会关闭能看到具体报错信息。逐行排查路径转换和wsl.exe调用语句。关于乱码主要是控制台代码页的问题。Windows 默认代码页可能是 936GBK而 WSL 输出的是 UTF-8 字符序列两者不对应就会出现中文乱码。脚本开头的chcp 65001 nul就是干这个的。但要注意如果 bat 文件里的提示文字本身是 UTF-8 编码而 cmd 在读取批处理时的代码页还是 936那么提示文字反而可能乱码。最稳妥的办法是bat 文件里的提示一律用英文或者把 bat 保存成 ANSI 编码只在执行 WSL 命令时切到 UTF-8。另外如果你的脚本里有需要交互式输入的地方比如read -p input: 拖拽运行也可以正常交互因为 wsl.exe 会继承当前 cmd 窗口的标准输入输出。你会在窗口里看到提示直接打字回车就行。唯一的限制是脚本执行结束后窗口会停在pause那里需要按任意键才会关闭这正好方便你查看输出。4.5 常见问题速查表现象原因解决办法拖拽后提示 Input 为空没有文件被正确传入确认直接拖 .sh 文件到 bat 图标上看%~1是否为空WSL 报$\r: command not foundCRLF 换行符脚本已自动执行sed -i s/\r$//如果还报错手动跑一次 sed输出中文乱码控制台代码页不对脚本开头有chcp 65001确认 bat 文件编码没有冲突找不到发行版发行版未安装或名字写错执行wsl -l -q查名字检查WSL_DISTRO配置窗口一闪而过批处理缺少 pause正常脚本已加 pause手动执行可看错误/mnt/C/xxx访问失败个别 WSL 版本对盘符大小写敏感改成小写盘符路径或者用wslpath -u转换5. 还能怎么玩不止是跑 .sh 脚本这套“拖拽 WSL 执行”的思路并不局限于 .sh 脚本。原理搞明白之后你可以把 bat 改成支持任意扩展名或者用同样的方式跑 Python 脚本wsl.exe -d %WSL_DISTRO% bash -lc python3 !linuxpath!也可以拖一个 jar 包过去让 WSL 里的 java 环境跑起来wsl.exe -d %WSL_DISTRO% bash -lc java -jar !linuxpath!我实际工作中用这个工具最多的场景是配合那些部署在 Linux 上的服务管理脚本。比如我有一台负责跑内部工具的机器经常要更新一个 Spring Boot 的 jar传统的操作是登录 Linux、把 jar 传到指定目录、执行 restart.sh。现在我直接在 Windows 这边把deploy.sh拖到 bat 上WSL 会自动挂载 Windows 目录、执行脚本脚本里再通过 scp 或 rsync 把 jar 推到目标机器整个过程不用手动进 WSL 窗口敲一行字。binwalk 也是我很常用的场景。Windows 下根本没有像样的 binwalk 体验WSL 里装好之后我的分析脚本写成一个 .sh拖进去就跑自动解包固件、输出分析报告。redis、elasticsearch 这类服务的启动/停止脚本同样适合这种玩法WSL 里装好服务Windows 这边写一个start.sh和stop.sh日常操作就像点按钮一样简单。这套方案对路径转换的要求很直接但如果你有更复杂的路径处理需求比如 UNC 路径或者网络驱动器推荐在 bat 里优先使用wslpath做转换会更可靠for /f delims %%P in (wsl.exe wslpath -u !winpath! 2^nul) do set linuxpath%%P我自己是先用字符串替换遇到特殊情况再手动用 wslpath 补一下两条路都留着工具就更通用。最后再分享一个细节。很多 .sh 脚本在开头会写#!/bin/bash这个 shebang 在 Linux 里是给内核看的。但当我们用bash script.sh的方式调用时shebang 并不重要。真正重要的是脚本是否有可执行权限。如果你在 Windows 下给脚本加了执行权限拖到 bat 里仍然走bash xxx.sh所以权限有没有都无所谓。但如果你哪天直接在 WSL 里执行./xxx.sh那就要注意先chmod x xxx.sh了。这一点经常被人忽略顺手记一下就行。我用这个拖拽运行工具已经攒了很久现在基本离不开它。最重要的收获不是省了多少敲命令的时间而是它彻底解决了 Windows 和 WSL 两个世界之间“路径转换”这个恼人的心智负担。你只需要关注脚本本身要干什么剩下的交给一个小小的 bat 文件去处理。如果你也在 WSL 里跑一堆脚本强烈建议花十分钟把这个工具搭起来相信我用上之后你就再也不想手敲那些又长又容易出错的命令了。