Same Command. Different Return Types.

Same Command. Different Return Types.

Rate This
  • Comments 11

My command seems to behave differently depending on how many items were returned.

This is something that the PowerShell Team hears from the community or our internal partners every couple of weeks. This blog post will explain when a Windows PowerShell command returns different types and offer some reasons for why it is done this way.

In general, Windows PowerShell commands can return one of three things:

  1. Null (not really a thing, I know)
  2. A single object of a particular type
  3. An array of objects (Object[])

Executing the commands below returned each of the types listed above. 

(Get-Service foobar).GetType()
(Get-Service NetLogon).GetType()
(Get-Service Net*).GetType()

Compare this to executing a WMI query in VBScript. Because you always get an array back, administrators have to litter their scripts with useless for-each loops. An extreme example is shown below, where the WMI query gets the Win32_OperatingSystem class to compute system uptime. It returns an array, even though there can never be multiple Win32_OperatingSystem items.

Set colOperatingSystems = objWMIService.ExecQuery _
    ("Select * from Win32_OperatingSystem")

For Each objOS in colOperatingSystems
    dtmBootup = objOS.LastBootUpTime
    dtmLastBootupTime = WMIDateStringToDate(dtmBootup)
    dtmSystemUptime = DateDiff("h", dtmLastBootUpTime, Now)
    Wscript.Echo dtmSystemUptime
Next

Since Windows PowerShell is also an interactive shell, we wanted to make it very easy for Administrators to work at the command line. A set of Windows PowerShell commands that do the same uptime calculation is shown below. Note that I didn’t have to do any indexing or iterating over a collection.

$os = Get-WmiObject Win32_OperatingSystem
$lastBootup = $os.ConvertToDateTime($os.LastBootUpTime)
$uptime = ((Get-Date) - $lastBootup).TotalHours
Write-Host $uptime

This behavior allows for some fairly lazy, err… convenient interactive console experiences. For example, let's say that I wanted to get the “Microsoft SharePoint Workspace Audit Service” that is installed on my machine.

Get-ServiceName
...
Mcx2Svc
Microsoft SharePoint Workspace Audit Service
MMCSS
MpsSvc

I can do that with very little typing:

$svc = Get-Service Mi*

Here I used a wildcard expression (that, in many cases, would return multiple values) but Windows PowerShell returned a single object that I can work with directly.

On the other hand, if you’re writing some scripts or managed code and want to guarantee that you get an array back, we offer the array cast operator @(). More details can be found in an old post by Bruce.

In closing: we can't (or at least shouldn't) try to infer what you meant, but at least we can make your life easier in a lot of cases by being smart about what we send back to you.

Travis Jones [MSFT]
Windows PowerShell PM
Microsoft Corporation

Leave a Comment
  • Please add 2 and 3 and type the answer here:
  • Post
  • I like how instead of fixing the issue they're "DEFENDING" it!!! Unbelieveable attitude!

    Consider this example:

    $myArray = Function-That-returns-Array

    $myArra[$index] will FAIL if the function decides to return a single item!!! so the code will work only sometimes!!! Only sometimes: when we have more than one item to return... if we have only one item to return you have to go through the whole "IF" junk! This is backward technology...

    arghhhh so frustrating to see such spins on what is an obvious flaw !! I'm furiouse!!!!!!!!!!!!!!!!!!!

  • haha, i thought so... filtering everything that may potentially have a negative imapct on this posting... Nice going!! no wonder why there are no comments here... no one had anytihng nice to say and got filtered out.... right on!

    Get your heads out of your you know whats and fix this problem instead of defending it! It's big huge problem.... can't think of how many times I've hit my head against the monitor for finding out what should have been an indexable array is now a string and even worse i end up indexing through the darn string!!

    Stop defending this ... honestly, it just makes you look stupid.... the professional thing to do here is admitting this was a really really REALLY stupid approach to something that wasn't even an issue.... I hate it when people try to re-invent wheel

  • If you want it to save you from yourself:

    set-psdebug -strict

  • Please mr 'The guy who..' and 'I thought so'....

    Do you mind re-reading Travis's story with an open mind?

    And, do you really have a bit of on-hands Powershell experience?

    $myArray = @(Function-That-returns-Array) will solve all your problems, assuming you wrote the function yourself or can be at least aware that the output has possibly one member.

    Since I got used to this PS "flaw" around 2 years ago I have never had any problem with it, but on the contrary a lot of ease of work.

    So give it another try for a few weeks.. you can always choose another tool if you wish anyway.

  • If you want to be sure that function will return array - make it do so:

    foo {

    ,@(1)

    }

    (foo).GetType().FullName

    System.Object[]

    You can also cast function results in @(), as mentioned in article.

    If you love arrays so much, you have always an option to avoid issues when the function can return none, one or more objects. Meanwhile - as mentioned in article - PowerShell is more than scripting language. When I play with it interactively and I ask for one object, I see no point in getting array instead every time. PowerShell should make it easier for interactive use just cause it's something you do more often and sometimes do not repeat given task. Adding 3 characters - @() - to a script which you write once and forget about what you actually wrote is zero work if you compare it with necessity to use foreach loop for any command that MAY return a collection.

  • You can wrap your function inside an @() block, so the Function-That-returns-Array returns ALWAYS an array.

    $myArray = @(Function-That-returns-Array)

    $myArray[$index] now works.

    I for myself like this behaviour. Developers like myself UNDERSTAND what's going on if calling such functions, but Administrators get confused if Get-Service NetLogon returns an array (and believe me they will).

  • Really guys, if you rely on a function returning an array, then how hard is it to wrap the result in @()??

    This was a design choice, not a bug.  If you spend as much time as I do punching out 5-liners in the console for quick admin and discovery then you'd really appreciate this feature.

  • I don't have much of a history in VBScript or other languages. PS is the first language I've really spent quality time learning because it's the first one to actually make sense to me as an admin. So, I'm not sure why this is a "big deal" and viewed as a problem. I've written hundreds of PS scripts and work with the CLI on a daily basis. Personally, I appreciate PS behaving this way as well as the dynamic type cast system, it makes my job easier. If you understand this is how PS works, no problem, and if you want to guarantee an array, the @() operator is easy enough. A system written for how the sys admin and IT pro work is going to be different than the traditional one written primarily from a developer mindset.

  • Try returning a [System.Data.DataTable] from a function :-)

    Yeah I do use ,@() to return arrays and it works fine... still it's kind of an extra step. but oh well...

    I'm just getting my head around thinking of Powershell as a scripting language; I'm kinda at fault here, because I keep on thinking of powershell as a programming language where I can perform object oriented programming... it's anything but. It's merely a scripting language to patch things up for immediate purposes until a good solution is available through proper software engineering approaches.

    I'm sorry if I sounded frustrated earlier... I do like powershell for what it is (way better than good old dos-command or whatever that unix/linux crap is...) if powershell wasn't such a powerfull scripting language  I wouldn't be tempted to do OOP with it :-) My fault...

    The array thing though... still annoying :)

    cheers!

  • To "The Guy":

    Yes you sounded frustrated earlier, but you clearly seem to have found the right spirit back...Congrats.

    I guess we are all struggling in the battlefield between what we can achieve already and what we aim at.

    That's why we have such a nice job...with new goals to hit every day and every year.

    Salud!

  • Im sorry for this comment and for my english. it is a little off-topic, but if it is possible, can you suggest some workarounds about an issue with bug in wsman cmdlets described here blog.whatsupduck.net/.../not-using-powershell-v2-to-view-wsman.html i reproduce this issue many times when working with centos+openwsman+sbib(fscb) usig basic authentication.

Page 1 of 1 (11 items)