Skip to content

Environment.CommandLine for published  #11305

Open

Description

@Spongman

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.

Activity

  1. RussKeldorph commented on Jan 14, 2019

    @RussKeldorph
    Contributor

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

  2. jeffschwMSFT commented on Jan 17, 2019

    @jeffschwMSFT
    Member

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

  3. mklement0 commented on Apr 18, 2019

    @mklement0

    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
  4. sdmaclea commented on Apr 22, 2019

    @sdmaclea
    Contributor

    @jeffschwMSFT I agree this should move to core-setup

  5. jkotas commented on Apr 22, 2019

    @jkotas
    Member

    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.

  6. sdmaclea commented on Apr 22, 2019

    @sdmaclea
    Contributor

    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.

  7. jkotas commented on Apr 22, 2019

    @jkotas
    Member

    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.

  8. danmoseley commented on Apr 26, 2019

    @danmoseley
    Contributor

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

  9. mklement0 commented on May 10, 2019

    @mklement0

    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)

  10. transferred this issue fromdotnet/coreclron Jan 31, 2020
  11. added this to the Future milestone on Jan 31, 2020
  12. Artoria2e5 commented on Aug 13, 2020

    @Artoria2e5

    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.

  13. mklement0 commented on Aug 16, 2020

    @mklement0

    @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 PowerShell/PowerShell#1995 (comment).

  14. added a commit that references this issue on Aug 19, 2020
  15. mklement0 commented on Oct 16, 2020

    @mklement0

    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.

  16. dotnet-policy-service commented on Apr 15, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @agocke, @elinor-fung
    See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    • Status
      No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions