You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
when you publish a linux-x64 command-line using self-contained mode, and then run the program using the supplied ELF exe, Environment.CommandLine (and Environment.GetCommandLineArgs()[0]) refers to the .dll instad of the ELF.
Reacted by Michael Klement, PRASOON KARUNAN V, 0x0309 and Mattwmaster58
There will be change required in CoreCLR to fix this. There may or may not be change required in core-setup too. It depends on the design. Maybe we should fix discuss what the fix is going to look like.
On a related note: Once this is fixed, will Environment.CommandLine / Environment.GetCommandLineArgs reflect an intentionally set custom invocation name (argv[0], reflected in $0 in POSIX-like shells), as used on Unix-like platforms?
Prepending the actual invocation name with - is a convention used to signal to POSIX-like shells such as Bash that they should act as login shells, and utilities such as ssh, macOS terminal emulators and Bash's exec -l use this mechanism.
For PowerShell to be a good Unix shell citizen (to be able to fully act as a user's default shell), it must therefore know its true invocation name, so it can check for - as the first character - see PowerShell/PowerShell-RFC#171.
A quick Bash example that shows that exec -l prepends - to the invocation name, which sh sees reflected in $0:
# Run from Bash.
$ (exec -l sh -c 'echo $0')
-sh
Also, if invocation happened via a symlink, it is the symlink's path that should be reported by Environment.CommandLine / Environment.GetCommandLineArgs: see #30212 (comment)
For some reason the reference from #25841 is not showing up. Adding it manually here.
The PowerShell test suite currently uses a C# testexe to validate some aspects of command-line passing, but due to this issue I am currently unable to verify that a "force quote" thing is working. Many Windows apps need that.
I think the only thing to be fixed here, if feasible, is that the actual executable (original entry point) not the managed DLL filename should be reflected in Environment.GetCommandLineArgs(0) / the first argument in Environment.CommandLine.
As @jkotas stated above, .NET Core reconstructs a hypothetical command line that may or may not exactly reflect the actual command line passed on Windows. On Unix, there is no command line to begin with.
(permalink to the source code).
On Windows, the reconstructed command line is fully equivalent to the original one, assuming that (a) you make the call without involvement of a shell (e.g., from the Windows Run dialog, WinKey-R) and (b) a .NET / Microsoft C/C++-compiler-based executable parses it.
On Unix, the reconstructed command line is not robustly usable in any real-world sense, because you'd need a shell to call it, and a shell's own up-front interpretation of such a command line would alter the call (e.g., foo '*.txt' would be reported as something like /path/to/foo *.txt - note the absence of quotes - which, if used as-is in a POSIX-like shell such as bash would cause globbing (expansion of wildcard pattern *.txt to matching filenames in the current dir)).
While calling the Windows API when running on Windows to get the true command line is feasible, I don't think it is worth the trouble to implement this in Environment.CommandLine.
For your use case, the simplest solution is to compile a .NET Framework console application, where the true command line is being reported; this is as simple as a single Add-Type call from Windows PowerShell; the following command creates a printArgs.exe executable in the current dir; when invoked, printArgs.exe prints its parsed arguments individually as well as the raw command line:
Add-Type-OutputType ConsoleApplication -OutputAssembly ./printArgs.exe-TypeDefinition @' using System; static class ConsoleApp { static int Main(string[] args) { Console.WriteLine("\n{0} argument(s) received (enclosed in <...> for delineation):\n", args.Length); for (int i = 0; i < args.Length; ++i) { Console.WriteLine(" <{0}>", args[i]); } Console.WriteLine("\nCommand line:\n\n {0}\n", Environment.CommandLine); return 0; } }'@
This is essentially what the dbea (Debug-ExecutableArguments) command from the Native PowerShell module does behind the scenes.
The alternative is to use a P/Invoke call to the Windows API's GetCommandLineW function, as demonstrated in PowerShell/PowerShell#1995 (comment).
A workaround for Environment.GetCommandLineArgs()[0] returning the wrong value is System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName (#42768 will make this simpler and more efficient in .NET 6.0: Environment.ProcessPath); the added advantage is always getting a full path back, whereas on Windows Environment.GetCommandLineArgs()[0] can situationally report a relative one.
Activity
RussKeldorph commentedon Jan 14, 2019
@jeffschwMSFT Can you attach an
area-label to this or move it to the repo it belongs? ...or dispose of it as you see fit. :)jeffschwMSFT commentedon Jan 17, 2019
cc @sdmaclea (we may need to copy this to core-setup)
mklement0 commentedon Apr 18, 2019
Note that this problem surfaces on all platforms.
E.g., using PowerShell Core v6.2 on Windows:
sdmaclea commentedon Apr 22, 2019
@jeffschwMSFT I agree this should move to core-setup
jkotas commentedon Apr 22, 2019
I am not sure about it.
There will be change required in CoreCLR to fix this. There may or may not be change required in core-setup too. It depends on the design. Maybe we should fix discuss what the fix is going to look like.
sdmaclea commentedon Apr 22, 2019
I had assumed that CoreCLR was populating
Environment.CommandLinefrom a property passed by AppHost to host policy.I was intending to change
AppHost, to correct the CommandLine.jkotas commentedon Apr 22, 2019
Environment.CommandLineis create by stitching the managed entrypoint .dll name and individual arguments together: https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/Environment.cs#L64. AppHost has no control over it.danmoseley commentedon Apr 26, 2019
fyi see linked issue for more details on how this works together.
mklement0 commentedon May 10, 2019
On a related note: Once this is fixed, will
Environment.CommandLine/Environment.GetCommandLineArgsreflect an intentionally set custom invocation name (argv[0], reflected in$0in POSIX-like shells), as used on Unix-like platforms?Prepending the actual invocation name with
-is a convention used to signal to POSIX-like shells such as Bash that they should act as login shells, and utilities such asssh, macOS terminal emulators and Bash'sexec -luse this mechanism.For PowerShell to be a good Unix shell citizen (to be able to fully act as a user's default shell), it must therefore know its true invocation name, so it can check for
-as the first character - see PowerShell/PowerShell-RFC#171.A quick Bash example that shows that
exec -lprepends-to the invocation name, whichshsees reflected in$0:Also, if invocation happened via a symlink, it is the symlink's path that should be reported by
Environment.CommandLine/Environment.GetCommandLineArgs: see #30212 (comment)Artoria2e5 commentedon Aug 13, 2020
For some reason the reference from #25841 is not showing up. Adding it manually here.
The PowerShell test suite currently uses a C#
testexeto validate some aspects of command-line passing, but due to this issue I am currently unable to verify that a "force quote" thing is working. Many Windows apps need that.mklement0 commentedon Aug 16, 2020
@Artoria2e5:
I think the only thing to be fixed here, if feasible, is that the actual executable (original entry point) not the managed DLL filename should be reflected in
Environment.GetCommandLineArgs(0)/ the first argument inEnvironment.CommandLine.As @jkotas stated above, .NET Core reconstructs a hypothetical command line that may or may not exactly reflect the actual command line passed on Windows. On Unix, there is no command line to begin with.
(permalink to the source code).
On Windows, the reconstructed command line is fully equivalent to the original one, assuming that (a) you make the call without involvement of a shell (e.g., from the Windows
Rundialog, WinKey-R) and (b) a .NET / Microsoft C/C++-compiler-based executable parses it.On Unix, the reconstructed command line is not robustly usable in any real-world sense, because you'd need a shell to call it, and a shell's own up-front interpretation of such a command line would alter the call (e.g.,
foo '*.txt'would be reported as something like/path/to/foo *.txt- note the absence of quotes - which, if used as-is in a POSIX-like shell such asbashwould cause globbing (expansion of wildcard pattern*.txtto matching filenames in the current dir)).While calling the Windows API when running on Windows to get the true command line is feasible, I don't think it is worth the trouble to implement this in
Environment.CommandLine.For your use case, the simplest solution is to compile a .NET Framework console application, where the true command line is being reported; this is as simple as a single
Add-Typecall from Windows PowerShell; the following command creates aprintArgs.exeexecutable in the current dir; when invoked,printArgs.exeprints its parsed arguments individually as well as the raw command line:This is essentially what the
dbea(Debug-ExecutableArguments) command from theNativePowerShell module does behind the scenes.The alternative is to use a P/Invoke call to the Windows API's
GetCommandLineWfunction, as demonstrated in PowerShell/PowerShell#1995 (comment).TestExe: add cmdline printing
mklement0 commentedon Oct 16, 2020
A workaround for
Environment.GetCommandLineArgs()[0]returning the wrong value isSystem.Diagnostics.Process.GetCurrentProcess().MainModule.FileName(#42768 will make this simpler and more efficient in .NET 6.0:Environment.ProcessPath); the added advantage is always getting a full path back, whereas on WindowsEnvironment.GetCommandLineArgs()[0]can situationally report a relative one.dotnet-policy-service commentedon Apr 15, 2026
Tagging subscribers to this area: @agocke, @elinor-fung
See info in area-owners.md if you want to be subscribed.