Stderr and stdout for powershell with Invoke-Expression has no output or errors
powershell, stderr, stdout
Solution
Note: The solution below assumes a well-behaved external utility: one that sends data to stdout, and error messages to stderr. If a given utility unexpectedly sends error messages to stdout instead, as appears to be the case for the OP, you must do your own parsing of stdout output in order to extract error messages.
$stdout = java -jar secretserver-jconsole.jar -s 123456 Password 2>($tmpFile=New-TemporaryFile)
$stderr = Get-Content $tmpFile; Remove-Item $tmpFile
`New-TemporaryFile` requires PSv5, but you can use `[io.path]::GetTempFileName()` in earlier versions.
Note that both `$stdout` and `$stderr` will contain a string array, with each item representing an output line.
The external utility's exit code is reflected in PowerShell's automatic `$LASTEXITCODE` variable.
Note that capturing stdout and stderr combined is actually easier:
$stdoutAndStdErr = java -jar secretserver-jconsole.jar -s 123456 Password 2>&1
Note: The stderr lines are each represented as a `[System.Management.Automation.ErrorRecord]` instance in the result, but they evaluate to the contents of the line in a string context.
As for what you tried:
There's generally no need to store a command in a string (and therefore rarely a need to use `Invoke-Expression`, whose use is generally to be avoided and can be risky).
- Either: invoke even external command lines directly (be mindful of possibly unwanted interpretation of the command line by PowerShell; in PSv3, `--%`, the stop-parsing token, turns off PowerShell's interpretation, but note its limitations).
- Or: If you need to store the full command in a variable first, use a script block (`$cmd = { ... }`), and invoke it later with `&` (call operator) or `.` (dot-sourcing operator) - the former creates a child scope for PS variables, the latter does not.
- Or: If you need to programmatically and conditionally construct the list of arguments to pass to a given external program, use an array, as described in this answer.
The `-OutVariable` and `-ErrorVariable` common parameters are only designed to work with PowerShell's output streams, not the outside world's stdout and stderr.
PowerShell captures stdout output as if it had been sent to PowerShell's success output stream, and treats the output lines as an array of strings.
Stderr output is passed through (and is not reflected in `$Error`), but you can redirect it to a file with `2>$file`, or you can merge it into the success stream with `2>&1` (in which case the stderr lines are each represented as a `[System.Management.Automation.ErrorRecord]` instance).
Problem
I am trying to get `stdout` and `stderr` from running a java process, and saw what seemed like an easy, cool way to do it, but it did not work: ``` $command = "java -jar secretserver-jconsole.jar -s 123456 Password" Invoke-Expression $command -OutVariable output -ErrorVariable errors Write-Host "stdout: $output" Write-Host "stderr: $errors" ``` Nothing displays with `$output` or `$errors`. I am using now: ``` $output = cmd /c $command ``` This does get me some `stdout`, but it is not ideal as I want `$error` message. I tried even this elaborate method, but it does not even seem to run the process I think as it returns almost instantly, and I know the normal process tasks several seconds. It also shows no output or error: ``` $psi = New-object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = 'java' $psi.Arguments = @("-jar","secretserver-jconsole.jar","- s","123456","Password") $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi [void]$process.Start() $output = $process.StandardOutput.ReadToEnd() $process.WaitForExit() Write-Host "stdout: $output" (tried: adding: do $process.StandardOutput.ReadLine() } while (!$process.HasExited) ```