12
votes

I have a script that runs an external EXE file. When that EXE file fails (sets errorlevel to 1), the PowerShell script fails.

I'm running curl.exe and am getting this:

  • CategoryInfo : NotSpecified: ( % Total % ... Time Current:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError

How can I ignore/catch the failure of the external EXE file and continue with my script?

6
Are you sure it is an external EXE returning an error code that is causing PowerShell to error? That doesn't normally cause an error to be thrown within PowerShell. In fact, you have to go out of your way to convert a $LASTEXITCODE that represents an error to a PowerShell error.Keith Hill
Could you at least post some code? Usually failure is not the default behavior in that case.Joey
I'm running curl.exe and getting this: + CategoryInfo : NotSpecified: ( % Total % ... Time Current:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandErrorripper234
Interesting. Apparently you are trying this via V2 Remoting? It seems that a native command can cause a PowerShell error in this case. I'm looking into it.Keith Hill
I'm not familiar with V2 remoting. I'm trying to run a local script containing the call to curl.ripper234

6 Answers

20
votes

This doesn't have anything to do with the exit code returned by the EXE. An error is generated when an EXE writes to stderr, but only within the ISE or when remoting or using background jobs.

An EXE that writes to stderr does not generate errors from the regular PowerShell command prompt. I'm not sure why this is the case.

17
votes

Actually, the application ran fine - PowerShell is mistaken in reporting an error.

When an application prints to standard error, PowerShell will sometimes conclude that application has failed. This is actually a design decision made by PowerShell developers. IMHO, this is a mistake, because many reliable applications (such as curl) print useful information to standard error in the course of normal operation. The consequence is that PowerShell only plays well with other PowerShell scripts and can't be relied on to interoperate with other applications.


Other readers in this thread had difficulty reproducing the behaviour because PowerShell implements it inconsistently. Whether the NativeCommandError occurs depends on how standard error is redirected (as a consequence the bug occurs in vanilla PowerShell ISE, but not vanilla PowerShell).

Whatever your opinion of the design decision in the first paragraph, the inconsistent implementation is for certain a PowerShell bug - see $LastExitCode=0 but $?=False in PowerShell. Redirecting stderr to stdout gives NativeCommandError.

1
votes

I was running the script through PowerShell ISE (an IDE), and I believe this is what caused the problems.

Running it through PowerShell itself seems to work.

0
votes

The error mentioned in the original post ("...NativeCommandError") appears when re-directing stderr to stdout, either e.g. by "...>file 2>&1", or by "*>file". As mentioned above, PowerShell produces this error if something is output via stderr, even if that 'something' is just normal logs (no errors). "CMake" e.g. uses stderr as normal trace output stream. While old Windows Command shell/script does NOT interpret any content on stderr as evidence of an error, PowerShell does, if the executable (e.g. CMake) is invoked with "Invoke-Expression", or called directly.

But!... if the executable is invoked with "Start-Process", this same problem does NOT occur.

Concretely, case 1:

Invoke-Expression cmake .... 1>$logFile1 2>$logFile2

OR

Invoke-Expression cmake .... *>$logFile

This leads to the above error

Case 2:

Start-Process cmake -ArgumentList $arguments ... -RedirectStandardOutput $logFile1 -RedirectStandardError $logFile2

The error doesn't appear. The files are 'clean', but two output files are populated

Conclusion: PowerShell behaves differently depending on how the executable is invoked

Powershell should preferably behave like the (old) Windows command shell, given the executables that are out there and that need to be run without experiencing such problems. The exit codes should hopefully suffice for qualifying errors. If this is really not possible, this feature of PowerShell - interpreting anything via stderr as error - should be switchable. Last but not least, the inconsistent behavior in that respect, and among the different means of invoking an executable, would best be avoided.

If you don't like streaming into two separate files as proposed in the case of 'Start-Process', the solution that works is to invoke the executable in the old command shell context, by use of "cmd /c" in the PowerShell script. Then you get both stdout and stderr streamed into ONE SINGLE file, AND properly populated (that is interleaved) by both streams as the executable operates. Example:

cmd /c "cmake ... >$logFile 2>&1"
0
votes

Sample script:

echo top
cmd /c dir foo   # doesn't exist
echo bottom

If run through the ISE, invoke-command, start-job, or start-threadjob, anything written to standard error will generate a remote exception. But the script will continue to run. This only applies to Powershell 5.

top
 Volume in drive C is DISK
 Volume Serial Number is 0123-4567

 Directory of C:\Users\js

cmd : File Not Found
At C:\Users\js\script.ps1:2 char:1
+ cmd /c dir foo 2>&1
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (File Not Found:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

bottom

One option is to hide the error with 2>$null

echo top
cmd /c dir foo 2>$null
echo bottom

Resulting in:

top
 Volume in drive C is DISK
 Volume Serial Number is 0123-4567

 Directory of C:\Users\js

bottom
0
votes

Since Powershell and Powershell ISE have different ways of processing and handling error output, I've found that the best approach in our environment is to use a command execution wrapper using the old Windows cmd.exe as follows:

$output = $(cmd /c "<<your exe here with parameters>>  2>&1")

This method does not require capturing the output to a file.

Once the process completes, the error status is available in the PowerShell variable:

$LASTEXITCODE