ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WSL拖拽运行.sh脚本:从手动敲命令到一键执行

WSL拖拽运行.sh脚本:从手动敲命令到一键执行 先说一下这个需求怎么来的。我自己平时在 Windows 上写点小工具、跑点数据分析脚本基本已经把 WSL Ubuntu 当作默认 Linux 环境了。Windows 这边写好的.sh脚本每次都要打开 WSL 终端手动敲bash /mnt/c/Users/xxx/script.sh路径又长又容易打错后来改成在文件管理器里把脚本文件直接拖进 WSL 终端窗口让系统自己把路径给我“送”进去再补齐一个bash前缀就行。测试过几次之后发现这个玩法还能进一步加工成“拖到图标上就自动执行”的效果于是就有了这篇内容。这篇文章写给两类人一类是刚接触 WSL、想把 Windows 上写好的 shell 脚本快速丢进 Linux 环境里跑起来的新手另一类是想把“手动敲命令”变成“拖拽一键执行”的进阶用户。里面所有方法我都实测过涉及 WSL 安装、路径转换、换行符坑、执行权限坑全部用大白话讲清楚照着抄作业即可。1. 先搞懂WSL 里的 SH 脚本是怎么跑起来的1.1 一条命令执行脚本bash、sh、./ 的区别在 WSL 里运行一个.sh脚本最常见的写法就三种bash script.sh sh script.sh ./script.sh前两种的区别在于解释器bash script.sh明确用bash来解析sh script.sh用的是系统默认的 POSIX shell在 Ubuntu 里通常是指向dash的。大多数日常脚本用bash跑最稳妥比如你用到了数组、[[ ]]判断这些 bash 特性sh可能直接报错。第三种./script.sh依赖文件头部的 shebang一般是#!/bin/bash还要求文件有执行权限。所以你在 Windows 下载了别人的脚本拖进 WSL 后先别想太多第一反应用bash script.sh就能覆盖 90% 的场景这也是为什么后面做“拖拽执行器”时我默认调用的都是bash而不是./script.sh。1.2 Windows 和 WSL 的路径阵营/mnt/c 和 wslpathWSL 里访问 Windows 的 C 盘路径永远是/mnt/c/...不是C:\...。比如桌面上的test.sh在资源管理器里的完整路径是C:\Users\你的用户名\Desktop\test.sh在 WSL 里对应的路径就是/mnt/c/Users/你的用户名/Desktop/test.sh这个对应关系是理解“拖拽运行”的核心因为拖拽操作的本质就是把 Windows 一侧的路径文本塞进 Linux 终端。如果路径格式不对bash 就找不到文件。WSL 自带一个路径转换命令wslpath专门做这件事wslpath C:\Users\你的用户名\Desktop\test.sh # 输出 /mnt/c/Users/你的用户名/Desktop/test.sh反向转换也支持wslpath -w /mnt/c/Users/你的用户名/Desktop/test.sh # 输出 C:\Users\你的用户名\Desktop\test.sh有了这个命令后面不管拖进来什么格式的路径都能转换成 WSL 能识别的样子。2. 三种拖拽姿势按使用习惯选2.1 直接拖进 Windows Terminal 的 WSL 标签页如果你用的是 Windows TerminalWin11 自带Win10 可以装这是最轻量的方式。操作很简单打开 Windows Terminal新建一个 WSL 标签页Ubuntu 或 Debian 之类的发行版。在资源管理器里选中你的.sh文件按住鼠标左键直接拖进终端窗口。松手后终端里会自动出现一长串路径文本比如C:\Users\你的用户名\Desktop\test.sh甚至可能带着引号。在路径前面加一个命令手动补全执行bash $(wslpath C:\Users\你的用户名\Desktop\test.sh)如果你拖进来时终端自动给路径加了引号比如显示成C:\Users\你的用户名\Desktop\test.sh带引号那就这样用bash $(wslpath C:\Users\你的用户名\Desktop\test.sh)我个人建议拖进来之后把 Windows 风格路径用wslpath包一层然后再交给bash执行。这套组合的本质是wslpath ...把 Windows 路径转成/mnt/c/...路径$(...)把转换结果作为参数传给 bash不过说实话这个方式每次还是要手动补一个命令前缀只能算“半个拖拽”。真正想省事还是看下面两种方案。2.2 拖到 BAT 上自动执行通用脚本启动器这是我最推荐的方式真·拖拽运行把.sh文件拖到一个全能型.bat文件图标上松手就自动完成换行符修正和执行。先新建一个文本文件改名为run-in-wsl.bat内容如下echo off setlocal enabledelayedexpansion for %%i in (%*) do ( set winfile%%~fi echo 正在准备: !winfile! for /f delims %%p in (wsl wslpath !winfile!) do set wslfile%%p echo 正在运行: !wslfile! wsl bash -lc sed -i s/\r$// !wslfile!; bash !wslfile! ) endlocal pause把这个run-in-wsl.bat放在桌面然后把.sh文件拖到它上面松开鼠标会弹出命令行窗口看到类似这样的输出正在准备: C:\Users\你的用户名\Desktop\test.sh 正在运行: /mnt/c/Users/你的用户名/Desktop/test.sh Hello from WSL一步步拆解for %%i in (%*)%*是拖拽时传入的所有文件参数一次可以拖多个文件。%%~fi获取拖进来文件的完整 Windows 路径。wsl wslpath !winfile!调用 Windows 系统里已安装的 WSL 子系统执行内部命令wslpath把 Windows 路径转成 WSL 路径再通过for /f捕获输出。sed -i s/\r$//顺手把脚本文件里的 CRLF 换行符清掉这是从 Windows 拖脚本进 Linux 最常见的坑后面详细说。bash !wslfile!正式执行脚本。这个小工具的核心思路BAT 只负责接收拖拽参数和路径转换真正干活的还是 WSL 内部的 Linux 命令。你完全不需要打开 WSL 窗口文件就会被自动传递进去并运行。2.3 拖给 PowerShell 发送到菜单右键一键运行如果你不喜欢 BAT 的黑窗口弹出来可以用 PowerShell 脚本配合 Windows 的“发送到”菜单实现右键文件 → 发送到 → 在 WSL 中运行。先创建run-in-wsl.ps1param( [Parameter(Mandatory $true, ValueFromRemainingArguments $true)] [string[]]$Paths ) foreach ($Path in $Paths) { $FullPath (Resolve-Path -LiteralPath $Path).Path $WslPath (wsl wslpath $FullPath).Trim() Write-Host Running: $WslPath wsl bash -lc sed -i s/\r$// $WslPath; bash $WslPath } Read-Host 按回车退出然后按Win R输入shell:sendto回车会打开“发送到”目录。把刚才的.ps1文件创建一个快捷方式放进去快捷方式的目标设置为powershell.exe -ExecutionPolicy Bypass -File C:\你的实际路径\run-in-wsl.ps1之后在任意.sh文件上右键 → 发送到 → 你的快捷方式名称就能在 WSL 里运行脚本。PS如果右键发送后 PowerShell 窗口一闪而过先别急着怀疑系统大概率是脚本路径写错了或者脚本文件本身有语法错误。3. 真正决定成败的三个细节3.1 CRLF 换行符最常见又最容易忽略的坑在 Windows 下写脚本最常见的编辑器记事本、VS Code、甚至某些工具生成的文本文件默认是 CRLF 换行也就是每一行结尾是\r\n。而 Linux 下 bash 只认 LF 换行也就是\n。如果脚本是 CRLF你直接运行会看到经典的报错bash: ./test.sh: /bin/bash^M: bad interpreter: No such file or directory或者更隐蔽的$\r: command not found这就是为什么上面两个执行器里我都加了这一句sed -i s/\r$// 文件路径它会把每行结尾的\r清掉相当于在 Windows 文件系统上就地做了 dos2unix 转换。如果你不喜欢 sed也可以用dos2unix命令dos2unix /mnt/c/Users/你的用户名/Desktop/test.sh但在 BAT 执行器里调用dos2unix需要额外安装所以我倾向于用系统自带的sed一次搞定。3.2 执行权限与 /mnt/c 挂载为什么 ./script.sh 可能失败很多人在 Windows 侧写好脚本拖进 WSL 后喜欢执行./script.sh然后遇到这个错误bash: ./script.sh: Permission denied原因有两层。第一层最直接脚本没有执行权限需要先执行chmod x script.sh第二层是 WSL 特有的坑。如果你把脚本放在 Windows 文件系统上比如C:\Users\...在 WSL 里访问的是 drvfs 挂载盘/mnt/c。drvfs 默认情况下对权限位的处理并不完全模拟 Linux 语义有时候你chmod x了但权限位显示正常执行./script.sh依然被拒绝。解决办法很简单二选一用bash script.sh代替./script.sh执行权限不是必须的把脚本复制到 WSL 自带的 Linux 文件系统里比如~/scripts/再chmod x。所以我的 BAT 执行器里直接写bash !wslfile!而不是./!wslfile!就是为了绕开执行权限这个变量。3.3 中文/空格路径的参数转义拖拽执行最怕路径里有空格和中文比如C:\Users\李四\Desktop\my script\测试.sh。空格的问题在于bash 默认按空白符切分参数路径里如果有空格必须用引号包住整个路径。中文的问题在于Windows 文件名在 WSL 里默认走 UTF-8大部分情况没问题但如果你用的工具不是 UTF-8 编码可能出现乱码。在 BAT 的for /f里我写了这样一段for /f delims %%p in (wsl wslpath !winfile!) do set wslfile%%p注意两个细节for /f delims表示不按空格分隔把整行内容都当一个变量这样 wslpath 输出的路径即使包含空格也不会被截断。调用wsl wslpath !winfile!时外层给!winfile!加了双引号这样路径里的空格不会把参数拆开。在 PowerShell 版本里我是用Resolve-Path -LiteralPath拿到完整路径后再传给 wslpath-LiteralPath不会把方括号之类特殊字符当作通配符处理中文和空格更稳。4. 拖拽失败排查常见问题与实测记录4.1 报错速查表我整理了一份自己在实际拖拽过程中遇到过的报错对照表方便快速定位报错信息大概原因解决办法/bin/bash^M: bad interpreter脚本 CRLF 换行sed -i s/\r$// 脚本路径command not found脚本里调用了 WSL 中没装的命令先apt install对应工具Permission denied无执行权限或 drvfs 权限位失效改用bash 脚本执行No such file or directory路径格式不对Windows 路径没转成/mnt/c用wslpath转换wsl: command not foundBAT 里调用 wsl 失败系统没有启用 WSL先跑wsl --install无法将...识别为 cmdletPowerShell 环境默认不识别某些命令检查命令是否在 PATH 中或用完整路径4.2 几个典型的踩坑现场先说我踩过最狠的一次用 VS Code 在 Windows 侧写了一个很长的部署脚本拖到 WSL 终端跑立刻报bad interpreter。当时我不知道是 CRLF还以为是 WSL 装坏了折腾半天。后来用file script.sh一看显示ASCII text, with CRLF line terminators真相大白。还有一个有意思的坑脚本里写了日志输出拖拽执行后终端没有任何输出。排查发现脚本前面的cd切换到了某个不存在的目录直接退出但 BAT 窗口里没有打任何错误。后来我在 BAT 里加了echo 正在运行: !wslfile!至少能确认脚本有没有被正确找到。再一个高频问题是很多人把脚本放在桌面上然后 BAT 放 D 盘拖拽后 BAT 的“当前目录”并不是脚本所在目录。所以我的 BAT 里用的是%%~fi拿完整路径而不是用%cd%拼接文件名这一点很关键。如果用%cd%\%%i很可能拼出错的路径。4.3 WSL1 和 WSL2 的拖拽差异如果你还在用 WSL1/mnt/c走的是 9P 协议更早的叫法可能模糊但性能差异存在从 Windows 拖.sh文件执行IO 性能明显比 WSL2 慢。拖拽本身没问题但脚本如果频繁读写文件耗时会比较长。WSL2 同样访问/mnt/c时跨文件系统读写依旧偏慢。如果你在意性能推荐把脚本复制到 WSL 的 Linux 文件系统里也就是~目录再执行。拖拽执行器主要解决的是“快速、临时地运行一个 Windows 侧脚本”如果是常驻型任务或大量 IO建议还是放 Linux 侧。5. 进阶把一个拖拽入口变成通用工具箱5.1 一个 BAT 同时拖入多个脚本文件前面 BAT 的代码已经用了for %%i in (%*)所以天然支持多文件拖拽。你可以在资源管理器里一次性选中a.sh、b.sh、c.sh一起拖到run-in-wsl.bat上它会逐个转换路径、逐个执行。如果你希望拖拽多个文件时按顺序执行而脚本之间有依赖关系建议在脚本内部做好判断或者把执行结果写入日志wsl bash -lc sed -i s/\r$// !wslfile!; bash !wslfile!; echo exit_code:$?这样每次执行完窗口里能看到退出码排查问题方便很多。5.2 拖拽执行器处理“jar 启动脚本”这类组合我见过不少场景是下载了一个压缩包里面既有.jar文件又有一个start.sh。你在 Windows 解压之后习惯性地把start.sh拖进 WSL 跑结果脚本里用的相对路径可能直接失效。比如某个服务包解压在C:\app\servicestart.sh和service.jar在同一目录。你打开 WSL 手动跑bash /mnt/c/app/service/start.sh脚本内部如果执行的是java -jar service.jar相对路径是相对于“当前工作目录”的而你在 WSL 登录时的工作目录是/home/你的用户名不是/mnt/c/app/service于是就会报找不到 jar。解决办法很简单在脚本开头加一行cd $(dirname $0)这行的意思是切到脚本自身所在目录。之后无论从哪个位置调用这个脚本里面的相对路径都正常了。用拖拽执行器时也一样如果你发现拖拽运行脚本后提示找不到同目录的文件多半就是这个相对路径问题。建议在自己写的脚本里统一加上cd $(dirname $0)这也是一个很好的习惯。5.3 把拖拽执行器接入 VS Code 和 Docker 场景VS Code 的 Remote-WSL 扩展可以让你直接在 WSL 里写脚本根本不需要拖拽。但如果你是从 Windows 侧双击.sh文件VS Code 默认可能用 Windows 关联程序打开而不是 WSL。这里有一个小技巧在 WSL 里跑一次code命令VS Code 会安装一个 WSL 专属的 shell 扩展之后在 WSL 目录下执行code script.sh会直接走 Remote-WSL 打开。至于 Docker Desktop如果你用的是 WSL2 后端容器内挂载目录通常也会涉及/mnt/c路径。拖拽执行器生成的路径是/mnt/c/...这个路径也能直接用于docker run -v挂载比如docker run -v /mnt/c/Users/你的用户名/Desktop/test.sh:/test.sh ubuntu bash /test.sh不过这类场景已经脱离“拖拽执行”本身了简单提一句就是告诉大家路径转换工具是通用的用熟了以后在 WSL、Docker、脚本之间来回倒腾文件就很顺。最后再分享一个个人习惯我通常会在 WSL 的~/.bashrc里加一个别名alias winbashbash $(wslpath $1)不算完美但写命令时能少敲几个字。不过说实话现在日常工作里我用的最多的反而是那个丢在桌面上的run-in-wsl.bat把脚本拖上去就完事不用开终端、不用手打命令连 Windows Terminal 都免了。这也是整篇内容里我最推荐先做的一个东西。
返回列表