Search code examples
powershell

Powershell: Start-Process with a file variable


c:\test.ps1 exists and is an empty file.

Start-Process -filepath powershell -argumentlist { -noexit -file "c:\test.ps1" }

opens a new powershell window and keeps it open (which is what I want).

$a = "c:\test.ps1"
Start-Process -filepath powershell -argumentlist { -noexit -file "$a" }

briefly flashes a new Powershell window and then closes it immediately. I wonder why?


Solution

  • tl;dr

    To pass pass-through arguments to Start-Process's -ArgumentList (-Args) parameter:

    $a = "c:\test.ps1"
    Start-Process powershell "-noexit -file `"$a`""
    

    Note: Parameter names -FilePath and -ArgumentList are positionally implied in this command; that is, the command is equivalent to Start-Process -FilePath powershell -ArgumentList "-noexit -file `"$a`""


    As for what you tried:

    Start-Process's -ArgumentList (-Args) parameter is [string[]] typed, i.e. it expects an array of string arguments to pass to the external program being launched - given that strings are the only supported data type when passing arguments to external programs.

    • Note: While passing pass-through arguments individually, as the elements of an array, to -ArgumentList (-ArgumentList '-noexit', '-file', $a) is conceptually the best approach, it is unfortunately ill-advised due to a long-standing bug in Start-Process, still present as of this writing (v7.2) - see GitHub issue #5576.
      For now, using a single string comprising all arguments, each enclosed in embedded "..." quoting if necessary (as shown above), is the only robust approach.
      As discussed in the linked GitHub issue, an -ArgumentArray parameter that supports robust array-based argument passing may be introduced in the future.

    When you pass a script block ({ ... }), it is stringified (its .ToString() method is implicitly called, and a script block's stringification is its verbatim contents (excluding the enclosing { and }), which means that no expansion (string interpolation) takes place;
    try { $HOME }.ToString(), for instance.

    • Note: You can use script blocks when you invoke powershell.exe directly, synchronously (rather than via Start-Process), but only from PowerShell, and such script blocks are executed by the callee, and therefore also do not see the caller's variables; you can, however, pass arguments to them; e.g.:
      powershell { "args passed: $args" } -args 1, 2