Home > Back-end >  Start-Process -RedirectStandardInput $null -FilePath powershell.exe -ArgumentList "myprogram.ex
Start-Process -RedirectStandardInput $null -FilePath powershell.exe -ArgumentList "myprogram.ex

Time:12-29

What's the powershell equivalent of unix "< /dev/null" when using start-process? In unix i would do: "myprogram.exe arg1 arg2 < /dev/null":

I tried the same in powershell using start-process:

 Start-Process -RedirectStandardInput $null -FilePath powershell.exe -ArgumentList "myprogram.exe arg1 arg2"

Error message, it doesn't work:

Start-Process : Cannot validate argument on parameter 'RedirectStandardInput'. The argument is null or empty. Provide an argument that is not null or
empty, and then try the command again.
At line:1 char:38
  Start-Process -RedirectStandardInput $null -FilePath powershell.exe - ...
                                       ~~~~~
      CategoryInfo          : InvalidData: (:) [Start-Process], ParentContainsErrorRecordException
      FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartProcessCommand

CodePudding user response:

There's got to be a better way:

PS> $temp = New-TemporaryFile
PS> start-process powershell.exe -ArgumentList "gci C:\" -nnw -wait -RedirectStandardInput $temp 

this works but it will create a million empty files in the temp folder to purge.

Another possibility:

PS> start-process powershell.exe -ArgumentList "`$null `| gci C:\" -nnw -wait

CodePudding user response:

With direct, synchronous invocation:

# Provide empty stdin input to myprogram.exe
# Equivalent to the following in a Unix shell:
#     myprogram.exe arg1 arg2 </dev/null
@() | myprogram.exe arg1 arg2

Note:

  • $null, as js2010 suggests, works too, but only with external programs - a PowerShell command would receive an actual $null as input.
  • By contrast, @() (an empty array) consistently sends nothing through the pipeline (external programs receive pipeline input via stdin), because it is the elements of the array that are being sent, one by one - if any - and an empty array by definition has no elements).

With an asynchronous-by-default Start-Process call:

Note:

  • Start-Process is only needed in unusual invocation scenarios, such as needing to launch a command in a new console window (including with elevation) or needing to run with a different user identity); for synchronous execution of CLIs with their streams connected to PowerShell's, use the direct-invocation technique shown above. For guidance on when Start-Process is or isn't appropriate, see GitHub docs issue #6239.

As of PowerShell 7.2, you can only pass file paths to Start-Process' -Redirect* parameters.

  • GitHub issue #5184 suggests a potential future enhancement that would also allow use of variables, using notation such as variable:null

Therefore, to effectively pass empty stdin input via Start-Process, you must indeed use -RedirectStandardInput with a (temporary) empty file (0 bytes), as shown in your own answer.

  • On Unix-like platforms, where you should be able to use -RedirectStandardInput /dev/null, that doesn't work as of PowerShell 7.2.1 due to a long-standing bug: instead of no stdin input, a newline is sent - see GitHub issue #8702. That said, Start-Process is rarely useful on Unix, due to its inability there to launch a process in a new window.

  • On Windows, deleting the temporary file unfortunately requires waiting for the launched program to exit, because the file is locked until then. Therefore, in order to reliably clean up a temporary file used this way you'll have to either use:

    • -Wait, to make the call synchronous
    • -PassThru, to have a System.Diagnostics.Process returned that you can later monitor for whether the process has exited (via .HasExited or .WaitForExit()).

However, there is a workaround: Call via cmd.exe and use its <NUL redirection (the equivalent of </dev/null in Unix):

# Parameters -FilePath and -ArgumentList positionally implied.
Start-Process cmd.exe '/c "myprogram.exe arg1 arg2 <NUL"'

Or, if you need a new PowerShell instance anyway, you can use its direct-invocation syntax in the command line you pass:

Start-Process powershell.exe '-c "@() | myprogram.exe arg1 arg2"'

Note:

  • The cmd-based workaround is faster and more light-weight, and therefore the better choice if the invocation of myprogram.exe is the only required operation.

  • More generally, delegating to cmd.exe's pipeline and redirection operators (and, analogously, to /bin/sh's on Unix) is a workaround to get around the limitation that PowerShell's pipelines currently cannot serve as raw byte conduits, resulting in data corruption if an attempt is made to capture raw byte output with >, for instance; ditto with Start-Process' -Redirect* parameters - see this answer.

  • With extra effort detailed in this answer, you can also achieve raw byte processing via the System.Diagnostics.Process-related .NET APIs shown below.


With direct use of the System.Diagnostics.Process-related .NET APIs:

Note:

  • You won't be able to launch the process in a new console window this way, because that would require the System.Diagnostics.ProcessStartInfo instance's .UseShellExecute property to be set to $true, which use of .RedirectStandardInput precludes.
# Configure the process to launch.
$ps = [System.Diagnostics.Process] @{
  StartInfo = [System.Diagnostics.ProcessStartInfo] @{ 
    FileName = 'myprogram.exe'
    Arguments = 'arg1 arg2'
    UseShellExecute = $false # Required for use of .Redirect* properties
    RedirectStandardInput = $true # Provide stdin input programmatically below
    WorkingDirectory = $PWD.ProviderPath # Use PowerShell's current dir.
  }
}

$null = $ps.Start() # Launch asynchronously
$ps.StandardInput.Close() # Close stdin, effectively providing no input.
$ps.WaitForExit()  # Wait for the process to exit.
  • Related