RootCommand.ExecutableName truncates dotted app names on Linux/macOS when targeting .NET 11
还没有人认领这个 Issue。
评估
调研方向
Start in src/command-line-api/src/System.CommandLine/RootCommand.cs, focusing on ExecutableName and ExecutablePath, then reproduce the published Contoso.Tool example on Linux or macOS. Compare net10.0 and net11.0 help output; done means dotted apphost names remain intact in RootCommand.ExecutableName and the Usage line.
由索引模型根据 Issue 内容生成。
描述
Summary
Starting with .NET 11, Environment.GetCommandLineArgs()[0] returns the name the apphost was invoked with instead of the managed .dll path (dotnet/runtime#131671). RootCommand.ExecutableName runs Path.GetFileNameWithoutExtension on that value. On Linux/macOS the apphost has no extension, so any app whose name contains a dot has its last segment treated as an "extension" and dropped. For example, Contoso.Tool becomes Contoso.
This affects the root command's Name, so it changes the Usage: line in help output. It also affects everything else derived from ExecutableName. Apps get this just by retargeting from net10.0 to net11.0, with no code changes.
Background: the .NET 11 runtime change
dotnet/runtime#131671, "Fix Environment.GetCommandLineArgs()[0] to report the host invocation name", merged 2026-08-10 and fixes dotnet/runtime#101837. It adds an ARGV0 host runtime property: hostpolicy captures the apphost's native argv[0], and CoreCLR uses it for element zero of Environment.GetCommandLineArgs(). From that PR:
| Command line | Environment.GetCommandLineArgs() |
|---|---|
dotnet.exe foo.dll 1 2 3 |
foo.dll, 1, 2, 3 (unchanged) |
foo 1 2 3 |
foo, 1, 2, 3 (previously the managed …/foo.dll path) |
../bar/special_foo.exe 1 2 3 (symlink to foo.exe) |
../bar/special_foo.exe, 1, 2, 3 |
Before this change, args[0] for an apphost launch always ended in .dll, so GetFileNameWithoutExtension happened to strip a real extension on every OS. Now it's the apphost name:
- Windows: the apphost name ends in
.exe, so stripping the extension is still correct. - Linux/macOS: the apphost has no extension, so a dotted name loses its last segment.
The System.CommandLine code involved
2.0.x servicing, e.g. 2.0.12 (dotnet/dotnet release/10.0.1xx, RootCommand.cs#L48-L54):
public static string ExecutableName
=> _executableName ??= Path.GetFileNameWithoutExtension(ExecutablePath).Replace(" ", "");
public static string ExecutablePath => _executablePath ??= Environment.GetCommandLineArgs()[0];
main (RootCommand.cs#L104-L137) has the same core logic: Path.GetFileNameWithoutExtension(path).Replace(" ", "") on L128, where path is Environment.GetCommandLineArgs()[0].
Repro
Contoso.Tool.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0;net11.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.12" />
</ItemGroup>
</Project>
Program.cs:
using System.CommandLine;
Console.WriteLine($"GetCommandLineArgs()[0] = {Environment.GetCommandLineArgs()[0]}");
Console.WriteLine($"RootCommand.ExecutableName = {RootCommand.ExecutableName}");
return new RootCommand("Sample tool").Parse(args).Invoke();
Publish for linux-x64 (I used SDK 11.0.100-rc.1.26425.128), then run ./Contoso.Tool --help on Linux.
net10.0
GetCommandLineArgs()[0] = /…/pub/net10.0/Contoso.Tool.dll
RootCommand.ExecutableName = Contoso.Tool
Description:
Sample tool
Usage:
Contoso.Tool [options]
net11.0
GetCommandLineArgs()[0] = ./Contoso.Tool
RootCommand.ExecutableName = Contoso
Description:
Sample tool
Usage:
Contoso [options]
On Windows the same net11.0 app still reports Contoso.Tool, because args[0] is Contoso.Tool.exe. The result is the same app showing different names per OS.
Who is affected
Any System.CommandLine app that:
- targets .NET 11 and is launched through its apphost rather than
dotnet app.dll, and - runs on Linux or macOS, and
- has a dot in its executable name. That's common, because the default
AssemblyNameis the project name, e.g.Contoso.Tool,MyCompany.Cli, or test projects likeFoo.Tests.
Impacts:
- Help output:
Usage:shows the wrong command name. - Other
ExecutableNameusers: code that usesRootCommand.ExecutableNamedirectly, and features that depend on it, such as completion/dotnet-suggestregistration, get the truncated name. A similar issue was fixed earlier forRegisterWithDotnetSuggestin #2316. - Tests: help-output snapshot tests become OS-dependent. We hit this in Aspire when retargeting our CLI tests to net11.0: help snapshots started failing only on Linux/macOS, where
Aspire.Cli.Testswas rendered asAspire.Cli(microsoft/aspire#20436). Our shippedaspirebinary isn't affected only because its name has no dot.
cc dotnet/runtime#131671, where I also asked whether the runtime change should be tagged as a breaking change: https://github.com/dotnet/runtime/pull/131671#issuecomment-5822378513
- 主要语言
- C#
- 星标
- 3.7k
- 派生
- 434
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
dotnet/command-line-api 的其他 Issue
-
German localization is incomplete可能已有人在做 @b-v-d-e-v 于 6 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 65/100
dotnet/command-line-api#2852 ·
-
Incomplete French (fr) translation: RequiredOptionWasNotProvided not translated可能已有人在做 @JPBlanc 于 104 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 65/100
dotnet/command-line-api#2822 · 1 条评论 ·
-
难度 2/5 1-3 小时 新手友好度 62/100
dotnet/command-line-api#2792 · 2 条评论 · 15 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 62/100
dotnet/command-line-api#2704 ·
-
GetCompletions should check exit code of invoked application可能已有人在做 @baradgur 于 1275 天前认领。 未关闭Area-Completions bug help wanted
难度 2/5 1-3 小时 新手友好度 72/100
dotnet/command-line-api#2137 · 1 条评论 · 3 个 reaction ·
查看 dotnet/command-line-api 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 76/100
microsoft/fluentui-blazor#5410 ·
维护者通常 1 天内回复
-
Bug pulumi/pulumi
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 82/100
activescott/lessmsi#306 ·
-
Docs MSBuild
难度 2/5 1-3 小时 新手友好度 68/100
getsentry/sentry-dotnet#5691 · 1 条评论 ·
维护者通常 2 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复