| 12 | */ |
| 13 | const executor = { |
| 14 | async windows (exec, { port }) { |
| 15 | // 主方案:PowerShell(更可靠,跨平台一致,Win7+ 默认可用) |
| 16 | try { |
| 17 | const cmds = [ |
| 18 | // 查找处于 Listen 状态的 TCP 连接并终止对应进程 |
| 19 | `$conn = Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Listen' } | Select-Object -First 1; if ($conn) { Stop-Process -Id $conn.OwningProcess -Force }`, |
| 20 | ] |
| 21 | await exec(cmds, { type: 'ps' }) |
| 22 | return true |
| 23 | } catch (psError) { |
| 24 | // 备选方案:CMD netstat + taskkill(Win7 无 pwsh 或 pwsh 执行失败时回退) |
| 25 | // 分两步执行,避免 for /f 在 cmd /s /c 下的引号解析问题 |
| 26 | try { |
| 27 | const output = await exec([`netstat -aon | find ":${port}"`], { type: 'cmd', printErrorLog: false }) |
| 28 | if (!output) { |
| 29 | throw new Error('没有找到占用该端口的进程') |
| 30 | } |
| 31 | |
| 32 | // 解析 netstat 输出,提取处于 LISTENING 状态的 PID |
| 33 | const lines = output.split(/\r?\n/) |
| 34 | let killed = false |
| 35 | for (const line of lines) { |
| 36 | if (!line.includes('LISTENING')) { |
| 37 | continue |
| 38 | } |
| 39 | const parts = line.trim().split(/\s+/) |
| 40 | const pid = parts[parts.length - 1] |
| 41 | if (pid && /^\d+$/.test(pid)) { |
| 42 | await exec([`taskkill /f /pid ${pid} /t`], { type: 'cmd', printErrorLog: false }) |
| 43 | killed = true |
| 44 | } |
| 45 | } |
| 46 | if (!killed) { |
| 47 | throw new Error('未找到处于 LISTENING 状态的进程') |
| 48 | } |
| 49 | return true |
| 50 | } catch (cmdError) { |
| 51 | // 两种方案都失败,抛出包含原始错误信息的异常 |
| 52 | throw new Error( |
| 53 | `终止占用端口 ${port} 的进程失败。\n` |
| 54 | + `PowerShell 方案: ${psError.message}\n` |
| 55 | + `CMD 方案: ${cmdError.message}`, |
| 56 | ) |
| 57 | } |
| 58 | } |
| 59 | }, |
| 60 | |
| 61 | async linux (exec, { port }) { |
| 62 | // 主方案:lsof |