Error Handling for Console Commands

by Aug 17, 2021

Occasionally it is useful and even necessary to use console applications in PowerShell scripts. In the previous tip, for example, we looked at ways to remove mapped network drives, and even though Remove-PSDrive claims to be able to do this, the most reliable way is to still use the old net.exe console command.

Let’s take a quick look at how you can check whether or not a console command completed successfully.

Let’s try that by mapping a new network drive, then removing it, with console applications. Of course, you can apply the same principles to any console application you may need to run within your PowerShell script:

 
PS> net use z: \\127.0.0.1\c$
The command completed successfully.


PS> net use z: /delete 
z: was deleted successfully.

PS> net use z: /delete 
net : The network connection could not be found.
At line:1 char:1
+ net use z: /delete
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (The network con...d not be found.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
More help is available by typing NET HELPMSG 2250.   
 

The mapped drive was created, then removed again. However, status messages are localized (so it is hard to compare them to expected values in multinational environments), and errors surface as exceptions.

By using $?, you can turn the output to a mere $true or $false: $true meaning the command completed successfully, and $false indicating (any) error:

 
PS> $null = net use z: \\127.0.0.1\c$; $result = $?; $result 
True

PS> $null = net use z: \\127.0.0.1\c$; $result = $?; $result 
net : System error 85 has occurred.
At line:1 char:9
+ $null = net use z: \\127.0.0.1\c$; $result = $?; $result
+         ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (System error 85 has occurred.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
The local device name is already in use.
False 
 

That’s much better because your script could then evaluate $result and in case of an error take appropriate steps or write messages to log files.

In case of errors, though, the detailed error information would still be emitted to the console, and there is no (apparent) way to remove it. Even full-blown try…catch error handlers do not respond to them:

 
PS> $null = try { net use z: \\127.0.0.1\c$} catch {}; $result = $?; $result 
net : System error 85 has occurred.
At line:1 char:15
+ $null = try { net use z: \\127.0.0.1\c$} catch {}; $result = $?; $res ...
+               ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (System error 85 has occurred.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
The local device name is already in use.
False  
 

The reason is: console applications do not throw .NET exceptions. When an error occurs, console applications just emit information via output stream #2.

And this coincidentally is the solution to wrap console applications: redirect all streams to the output stream. This maps a network drive: the first call succeeds; all subsequent calls fail:

 
PS> $null = net use z: \\127.0.0.1\c$ *>&1; $?
True

PS> $null = net use z: \\127.0.0.1\c$ *>&1; $?
False

PS> $null = net use z: \\127.0.0.1\c$ *>&1; $?
False   
 

Likewise, this removes the mapped drive again, and like before the first call succeeds, all remaining calls fail:

 
PS> $null = net use z: /delete *>&1; $result = $?; $result 
True

PS> $null = net use z: /delete *>&1; $result = $?; $result 
False

PS> $null = net use z: /delete *>&1; $result = $?; $result 
False  
 


Twitter This Tip! ReTweet this Tip!