From 19fa4d5e9941052c7d99f01f3b8735e16da83894 Mon Sep 17 00:00:00 2001 From: stoney Date: Tue, 28 Apr 2026 15:35:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(commandLauncher):=20Linux=20=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=94=AF=E6=8C=81=E5=90=AB=E5=8F=82=E6=95=B0=E7=9A=84?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 parseCommandString 解析带引号和空格的命令字符串 - 拆分可执行文件路径与参数后分别用于窗口匹配和 spawn 启动 --- .../core/commandLauncher/linuxLauncher.ts | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/main/core/commandLauncher/linuxLauncher.ts b/src/main/core/commandLauncher/linuxLauncher.ts index a17b8fd1..5d0a3df7 100644 --- a/src/main/core/commandLauncher/linuxLauncher.ts +++ b/src/main/core/commandLauncher/linuxLauncher.ts @@ -4,6 +4,39 @@ import { WindowManager } from '../native' import type { ConfirmDialogOptions } from './types' import fs from 'fs' +/** + * 将可能包含参数的命令字符串拆分为 [可执行文件, ...参数列表] + * 例如: "/usr/bin/ghostty --gtk-single-instance=true" → ["/usr/bin/ghostty", "--gtk-single-instance=true"] + */ +function parseCommandString(cmd: string): [string, string[]] { + const parts: string[] = [] + let current = '' + let inQuote: string | null = null + + for (let i = 0; i < cmd.length; i++) { + const ch = cmd[i] + if (inQuote) { + if (ch === inQuote) { + inQuote = null + } else { + current += ch + } + } else if (ch === '"' || ch === "'") { + inQuote = ch + } else if (/\s/.test(ch)) { + if (current) { + parts.push(current) + current = '' + } + } else { + current += ch + } + } + if (current) parts.push(current) + + return [parts[0], parts.slice(1)] +} + export async function launchApp( appPath: string, confirmDialog?: ConfirmDialogOptions @@ -28,6 +61,9 @@ export async function launchApp( } } + // 拆分可执行文件路径和参数 + const [executable, args] = parseCommandString(appPath) + // Linux 平台窗口激活增强: // 先尝试寻找已经打开的窗口并激活它 try { @@ -45,7 +81,7 @@ export async function launchApp( // 检查该进程的可执行文件是否匹配 const exePath = fs.readlinkSync(`/proc/${pid}/exe`) // 检查路径是否匹配(考虑到软链接或相对路径,对比真实绝对路径) - if (exePath === fs.realpathSync(appPath)) { + if (exePath === fs.realpathSync(executable)) { console.log(`[Launcher] 发现应用已运行 (PID: ${pid}), 尝试通过 WID ${wid} 激活窗口`) WindowManager.activateWindow(wid) return resolve(true) @@ -72,7 +108,7 @@ export async function launchApp( // 使用 spawn + detached: true + stdio: 'ignore' // 这样不会阻塞主进程,且不会因为应用退出码非 0 而报错(常见于 WeChat 等应用) try { - const child = spawn(appPath, [], { + const child = spawn(executable, args, { detached: true, stdio: 'ignore' })