为什么在 运行 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 是基于它构建的(在撰写本文时两者都处于预览状态,但我不要指望这些版本会及时修复。
但是,有一个解决方法实际上通常更可取:
[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
我 运行 以下命令作为测试代码的一部分,试图确定脚本是 运行 在交互式会话中(即使用 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 是基于它构建的(在撰写本文时两者都处于预览状态,但我不要指望这些版本会及时修复。
但是,有一个解决方法实际上通常更可取:
[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