A PowerShell Puzzler

It has been said that you can write BASIC code in any language. When I look at PowerShell code, I tend to see a lot of code that looks like transplanted C# code. It’s easy to get confused sometimes, since C# and PowerShell syntax are similar, and when you are dealing with .NET framework objects the code is often nearly identical. Most of the time, though, the differences between the semantics are small and there aren’t a lot of surprises.

I recently found one case, however, that stumped me for a while. What makes it more painful is that I found it while conducting a PowerShell training session and was at a loss to explain it at the time. Please read the following line and try to figure out what will happen without running the code in a PowerShell session.

$services=get-wmiobject -class Win32_Service -computername localhost,NOSUCHCOMPUTER -ErrorAction STOP

.
.
.
.
You’re thinking about this, right?
.
.
.
.
.
.
Once you’ve thought about this for a few minutes, throw it in a command-line somewhere and see what it does.

The first thing (I think) that’s important to notice is that the behavior is completely different from anything that you will see in any other language (at least in my experience).

In most languages, if you have an assignment statement and a function call one of three things will happen:

  1. The assignment statement is successful (i.e. the variable will be set to the result of the function call)
  2. The function call will fail (and throw an exception), leaving the variable unchanged
  3. The assignment could fail (due to type incompatibility), leaving the variable unchanged

In PowerShell, though, we see a 4th option.

  • The function call succeeds for a while (generating output) and then fails, leaving the variable unchanged but sending output to the console (or to be captured by an enclosing scope).

Here’s what the output looks like when it’s run (note: I abbreviated some to make the command fit a line):
Puzzler

Not shown in the screenshot is that at the end of the list of localhost services is the expected exception.

How this makes sense is that an assignment statement in PowerShell assigns the final results of the pipeline on the RHS to the variable on the LHS. In this case, the pipeline started generating output when it used the localhost parameter value. As is generally the case with PowerShell cmdlets, that output was not batched. When the get-wmiobject cmdlet tried to use the NOSUCHCOMPUTER value for the ComputerName parameter, it obviously failed and since we specified -ErrorAction Stop, the pipeline execution immediately terminated by throwing an exception. Since we didn’t reach the “end” of the pipeline, the assignment never happens, but there is already output in the output stream. The rule for PowerShell is that any data in the output stream that isn’t captured (by piping it to a cmdlet, assigning it, or casting to [void]) is sent to the console, so the localhost services are sent to the console.

It all makes sense if you’re wearing your PowerShell goggles (note to self—buy some PowerShell goggles), but if you’re trying to interpret PowerShell as any other language this behavior is really unexpected.

Let me know what you think. Does this interpretation make sense or is there an easier way to see what’s happening here?

-Mike

7 Comments

  1. I actually got what you expected: it threw an error, didn’t change the variable, and didn’t output anything (other that the error) to the console.

    My system also has PSv4 installed on it (comes with 8.1), so I fired up a couple of servers (a terminal server with v3, and an Exchange 2010 box I’ve had to keep at v2) to try it on those. I got the same thing in v2, but v3 acted exactly as you saw it.

    So, what you found is NOT the PowerShell way of thinking, but a bug that was introduced at version 3, and fixed in version 4.

    • This is really strange. I just logged into several servers (and a couple of laptops) running various OSes and versions of PowerShell and I got the exact same behavior in all of them, starting with PowerShell 1.0 on a Server 2003 box all the way up to 4.0 on a 2012R2 and 8.1 box.

      • By the way, I really do appreciate you taking the time to comment on this. I’m not sure why I’m seeing different things in my PowerShell sessions than you are. Personally, I’d like it to be a bug (since I didn’t expect the result at all), but I don’t think it is.

    • Can you post the OS and PowerShell version you’re running and a screenshot? I’m really confused by this since I haven’t found an environment where I don’t get the result I described here. Also, to make the screenshot more meaningful you could use the Win32_OperatingSystem class (since it only returns one instance instead of listing all the services).

    • Can you reply with the OS and PowerShell version # that you’re using? Can you post a screenshot? I’m really confused since I haven’t found an environment that doesn’t behave as I’ve described here. As a side note, using Win32_OperatingSystem allows the command and output to be in the same screenshot. Don’t know why I didn’t think of making that change when I wrote the article.

      • Never mind. I realized (by looking at the screenshots I took of the various systems) that I somehow missed the “$services=” when I copied the command around. So I’m troubled that I wasn’t paying attention when I thought that I was, but I’m a bit relieved that this might just be a bug in 3.0.

Comments are closed.