Skip to content

Commit 582baac

Browse files
committed
处理好可执行程序的查找逻辑
1 parent 319b52c commit 582baac

File tree

1 file changed

+39
-33
lines changed

1 file changed

+39
-33
lines changed

src/DotNetCampus.ModelContextProtocol/Transports/Stdio/StdioClientTransport.cs

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,6 @@ private async Task RunLoopAsync(StdioProcessInfo stdio, CancellationToken cancel
169169
};
170170
}
171171

172-
/// <summary>
173-
/// 跨平台解析命令路径。<br/>
174-
/// 如果命令是完整路径或相对路径,直接返回;<br/>
175-
/// 否则在 PATH 环境变量中查找命令。
176-
/// </summary>
177172
static string? ResolveCommandPath(string command)
178173
{
179174
// 如果命令包含路径分隔符,说明是路径而非命令名
@@ -189,48 +184,40 @@ private async Task RunLoopAsync(StdioProcessInfo stdio, CancellationToken cancel
189184
return null;
190185
}
191186

187+
// 尝试提取命令里原本的扩展名(例如用户可能指定的是 npx/dnx 也可能指定的是 npx.cmd/dnx.exe。
188+
var extensionInCommand = Path.GetExtension(command);
192189
var paths = pathEnv.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
193-
var extensions = GetExecutableExtensions();
190+
var extensions = ExecutableExtensionsLazy.Value;
194191

195192
foreach (var path in paths)
196193
{
197-
// 在 Windows 上,尝试命令名 + 各种扩展名
198-
foreach (var extension in extensions)
194+
if (extensions.Count is 0)
199195
{
200-
var fullPath = Path.Combine(path, command + extension);
196+
// Linux 等无扩展名的可执行程序。
197+
var fullPath = Path.Join(path, command);
201198
if (File.Exists(fullPath))
202199
{
203200
return fullPath;
204201
}
202+
continue;
205203
}
206-
}
207-
208-
return null;
209-
}
210204

211-
/// <summary>
212-
/// 获取可执行文件扩展名列表(跨平台)。
213-
/// </summary>
214-
static string[] GetExecutableExtensions()
215-
{
216-
if (OperatingSystem.IsWindows())
217-
{
218-
// Windows 上从 PATHEXT 环境变量获取可执行扩展名
219-
var pathExt = Environment.GetEnvironmentVariable("PATHEXT");
220-
if (!string.IsNullOrEmpty(pathExt))
205+
// Windows 等带扩展名的可执行程序。
206+
foreach (var extension in extensions)
221207
{
222-
var extensions = pathExt.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
223-
// 确保包含空扩展名(用于无扩展名的可执行文件)
224-
return [.. extensions];
208+
var fullPath = extensionInCommand?.Equals(extension, StringComparison.OrdinalIgnoreCase) is true
209+
// 如果命令自带的扩展名正好与环境变量里的相同,说明命令本身确实带的是扩展名。
210+
? Path.Join(path, command)
211+
// 否则,叠加环境变量里的扩展名。
212+
: Path.Join(path, command + extension);
213+
if (File.Exists(fullPath))
214+
{
215+
return fullPath;
216+
}
225217
}
226-
// 默认 Windows 可执行扩展名
227-
return [".exe", ".cmd", ".bat", ".com"];
228-
}
229-
else
230-
{
231-
// Unix 系统上可执行文件通常没有扩展名
232-
return [""];
233218
}
219+
220+
return null;
234221
}
235222
}
236223

@@ -249,6 +236,25 @@ await Task.Run(() =>
249236
});
250237
}
251238

239+
private static readonly Lazy<IReadOnlyList<string>> ExecutableExtensionsLazy = new Lazy<IReadOnlyList<string>>(() =>
240+
{
241+
if (!OperatingSystem.IsWindows())
242+
{
243+
// Unix 系统上可执行文件通常没有扩展名
244+
return [""];
245+
}
246+
// Windows 上从 PATHEXT 环境变量获取可执行扩展名
247+
var pathExt = Environment.GetEnvironmentVariable("PATHEXT");
248+
if (string.IsNullOrEmpty(pathExt))
249+
{
250+
return [".exe", ".cmd", ".bat", ".com"];
251+
}
252+
var extensions = pathExt.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
253+
// 确保包含空扩展名(用于无扩展名的可执行文件)
254+
return extensions;
255+
// 默认 Windows 可执行扩展名
256+
}, LazyThreadSafetyMode.None);
257+
252258
private readonly record struct StdioProcessInfo
253259
{
254260
public required Process Process { get; init; }

0 commit comments

Comments
 (0)