mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
feat(shell-tool): honor user $SHELL on POSIX
This commit is contained in:
parent
2cd51743d1
commit
82ed239b5b
@ -11,9 +11,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 内置工具:本地命令执行(跨平台)
|
||||
@ -130,7 +132,10 @@ public class ShellExecuteTool {
|
||||
* Windows: cmd.exe /D /S /C "command"
|
||||
* /D 禁用 AutoRun 注册表项,避免副作用
|
||||
* /S 保留引号原样传递给命令
|
||||
* Unix: /bin/sh -c command
|
||||
* Unix: $SHELL -c command (RFC-03 Lane A1, fixes QwenPaw #3767)
|
||||
* honors the user's interactive shell so alias resolution / PATH
|
||||
* from the calling environment still apply; falls back to /bin/sh
|
||||
* when $SHELL is unset or points at a non-executable path.
|
||||
*/
|
||||
private static ProcessBuilder buildShellProcess(String command) {
|
||||
ProcessBuilder pb;
|
||||
@ -138,7 +143,8 @@ public class ShellExecuteTool {
|
||||
String winCommand = sanitizeWindowsCommand(command);
|
||||
pb = new ProcessBuilder("cmd.exe", "/D", "/S", "/C", winCommand);
|
||||
} else {
|
||||
pb = new ProcessBuilder("/bin/sh", "-c", command);
|
||||
String shell = selectPosixShell(System.getenv("SHELL"));
|
||||
pb = new ProcessBuilder(shell, "-c", command);
|
||||
}
|
||||
|
||||
// 设置工作区活动目录
|
||||
@ -181,6 +187,49 @@ public class ShellExecuteTool {
|
||||
return command.replace("\r\n", " ").replace("\n", " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC-03 Lane A1 — pick the POSIX shell binary to invoke for a non-Windows
|
||||
* tool call (fixes QwenPaw #3767).
|
||||
*
|
||||
* <p>Returns {@code userShellEnv} verbatim when:
|
||||
* <ul>
|
||||
* <li>the value is non-blank,</li>
|
||||
* <li>parses as a valid path,</li>
|
||||
* <li>and the resolved binary is executable.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Otherwise falls back to {@code /bin/sh} — the legacy hardcoded
|
||||
* default. Important: {@code /bin/sh} on Debian/Ubuntu is dash, which
|
||||
* does NOT honor users' bash-isms; the whole point of this method is
|
||||
* to prefer the user's actual interactive shell when one is configured.
|
||||
*/
|
||||
static String selectPosixShell(String userShellEnv) {
|
||||
return selectPosixShell(userShellEnv, Files::isExecutable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test seam — same logic as {@link #selectPosixShell(String)} but with
|
||||
* an injectable executable check so unit tests can drive every branch
|
||||
* without depending on which shells actually exist on the test runner
|
||||
* (Windows CI has no {@code /bin/sh}, POSIX dev hosts have varying
|
||||
* shells installed). Production callers go through the single-arg
|
||||
* overload above.
|
||||
*/
|
||||
static String selectPosixShell(String userShellEnv, Predicate<Path> executableCheck) {
|
||||
if (userShellEnv == null || userShellEnv.isBlank()) {
|
||||
return "/bin/sh";
|
||||
}
|
||||
try {
|
||||
Path candidate = Path.of(userShellEnv);
|
||||
if (executableCheck.test(candidate)) {
|
||||
return userShellEnv;
|
||||
}
|
||||
} catch (InvalidPathException ignored) {
|
||||
// Some exotic $SHELL value that isn't a path — fall through to default.
|
||||
}
|
||||
return "/bin/sh";
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复 LLM 常见的 Windows 命令转义问题。
|
||||
* LLM 有时会产生 bash 风格的反斜杠转义引号 (\"),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user