为什么在 运行 pwsh.exe 时 GetCommandLineArgs return 是一个 DLL?

Why does GetCommandLineArgs return a DLL when running pwsh.exe?

我 运行 以下命令作为测试代码的一部分,试图确定脚本是 运行 在交互式会话中(即使用 CLI)还是来自无控制台远程会话(SSH、SCP、FTP)。

因此,在 Powershell Core (pwsh.exe) CLI 会话中使用 [Environment]::GetCommandLineArgs() 时,我得到:C:\Program Files\PowerShell\pwsh.dll。这令人惊讶,因为我本以为会得到 pwsh.exe 而不是 DLL。

为什么我得到的是DLL而不是EXE?
怎么回事?

这是一个已知问题,截至 .NET 5.0 仍未修复,PowerShell Core 7.1 是基于它构建的(在撰写本文时两者都处于预览状态,但我不要指望这些版本会及时修复。

GitHub issue #11305

但是,有一个解决方法实际上通常更可取:

[System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName

# Shorter, but slower equivalent (works only from PowerShell):
#  (Get-Process -Id $PID).MainModule.FileName

[System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName 优于 [Environment]::GetCommandLineArgs()[0] 的原因有两个:

  • 前者总是报告完整路径。

  • 至少到 .NET Core 3.1,后者可以报告一个临时位置,即 single-file .NET Core 可执行文件提取他们自己到幕后的这些地点。

请注意 .NET 5.0+ 将有一个专用的 [Environment]::ProcessPath 属性,它的性能也会更好 - 请参阅 GitHub PR #42768.


如果您想获得 [Environment]::GetCommandLineArgs() 更正 版本 - 也就是说,真正的可执行文件存储在索引 0 和真正的 arguments 剩余元素:

# Get the original array...
# Note: Modifying the array directly works, but you can enclose 
#       the call in @(...) in order to create a *copy* of it.
$cmdLineArgs = [Environment]::GetCommandLineArgs()
# ... and fix the entry for the executable (index 0)
$cmdLineArgs[0] = [Diagnostics.Process]::GetCurrentProcess().MainModule.FileName

通过从 cmd.exe 调用 PowerShell CLI 进行演示:

C:>pwsh -noprofile -c "$a=[Environment]::GetCommandLineArgs(); $a[0]=[Diagnostics.Process]::GetCurrentProcess().MainModule.FileName; $a"
C:\Program Files\PowerShell\pwsh.exe
-noprofile
-c
$a=[Environment]::GetCommandLineArgs(); $a[0]=[Diagnostics.Process]::GetCurrentProcess().MainModule.FileName; $a