Runtime: Environment.CommandLine for published

Created on 24 Oct 2018  路  12Comments  路  Source: dotnet/runtime

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.

area-AssemblyLoader-coreclr

All 12 comments

@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. :)

cc @sdmaclea (we may need to copy this to core-setup)

Note that this problem surfaces on all platforms.

E.g., using PowerShell Core v6.2 on Windows:

PS> [Environment]::GetCommandLineArgs()
C:\Program Files\PowerShell\6\pwsh.dll

@jeffschwMSFT I agree this should move to core-setup

move to core-setup

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.

I am not sure about it.

I had assumed that CoreCLR was populating Environment.CommandLine from a property passed by AppHost to host policy.

I was intending to change AppHost, to correct the CommandLine.

Environment.CommandLine is 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.

fyi see linked issue for more details on how this works together.

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 https://github.com/PowerShell/PowerShell-RFC/pull/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 https://github.com/dotnet/runtime/issues/30212#issuecomment-710357008

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.

@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 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 https://github.com/PowerShell/PowerShell/issues/1995#issuecomment-677664318.

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 for 5.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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GitAntoinee picture GitAntoinee  路  3Comments

bencz picture bencz  路  3Comments

yahorsi picture yahorsi  路  3Comments

jamesqo picture jamesqo  路  3Comments

Timovzl picture Timovzl  路  3Comments