Search code examples
powershellerror-handlingstdoutstderr

Powershell get output from rabbitmqctl and parse exception


I'm calling rabbitmqctl from Powrshell:

$out = (& $path ($arguments -split " ") )
Write-Host $out

With a correct path, the output has a success message:

Clearing policy "<policyname>" on vhost "<vhost>" ...

With an incorrect path, I get an exception:

....
    + CategoryInfo          : NotSpecified: (Error::String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
Parameter does not exist

The message Clearing policy is missing. When stopping the execution, the $_.Exception.Message is empty.

try {
   $ErrorActionPreference = 'Stop'
   ...
}
catch {
    Write-Warning $_.Exception.Message
}

Is there a way to get both the Clearing policy message and the last line Parameter does not exist?


Solution

  • tl;dr: Redirect the error stream to stdout and convert each element to string:

    $out = (& $path ($arguments -split " ") 2>&1) | % ToString
    

    When executing external programs (like rabbitmqctl), you will not receive any exceptions. You may get output in different streams (mostly stdout and stderr), and your external program will return an error code, but certainly not an exception, PowerShell could process.

    I guess you are using PowerShell ISE. Why? Because in contrast to PowerShell, PowerShell ISE has the strange behaviour to throw an exception if it is forced to print text, that has been received from the error stream of an external program.

    You can reproduce it with the following command:

    where.exe nonExistentExecutable.bla
    

    In PowerShell, it will output some text saying, that it could not find a file with that pattern. In PowerShell ISE, you will get a NativeCommandError exception, that contains the text from the error stream.

    The key point here is, that most executables write to the error stream, if they have to report errors. So does where.exe and so does rabbitmqctl in your case. You cannot capture the error stream like that:

    $out = (& $path ($arguments -split " ") )
    

    This will only capture the success stream (aka stdout). The error stream will be forwarded to the console, and ISE will throw an exception. That's what's happening in your case. To also capture the error stream, redirect it to stdout:

    $out = (& $path ($arguments -split " ") 2>&1)
    

    Now, $out will definitely capture the result, either the success message or the error message. In case of the error stream, each line will be wrapped by a [System.Management.Automation.ErrorRecord] and even PowerShell (like ISE) would output that in form of an exception. To prevent that, convert every element to string:

    $out = (& $path ($arguments -split " ") 2>&1) | % ToString
    

    This won't have negative impact on success messages and thus can be used in any case.