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)
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).
--%
, the stop-parsing token, turns off PowerShell's interpretation, but note its limitations).$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.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).