Invoke-Command -ScriptBlock returns null rather than empty array

M S 21 Reputation points
2020-08-29T05:32:13.907+00:00

I am trying to run supplied commands in an Invoke-Command -ScriptBlock and retrieve the return value. However returning an empty array, @() , results in null being returned (or at least what is stored in the receiving variable).

Example:

PS C:\> $ret = @()  
PS C:\> $ret.GetType()  
  
IsPublic IsSerial Name                                     BaseType  
-------- -------- ----                                     --------  
True     True     Object[]                                 System.Array  
  
  
PS C:\> $ret = Invoke-Command -ScriptBlock {return @()}  
PS C:\> $ret.GetType()  
You cannot call a method on a null-valued expression.  
At line:1 char:1  
+ $ret.GetType()  
+ ~~~~~~~~~~~~~~  
	+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException  
	+ FullyQualifiedErrorId : InvokeMethodOnNull  

Whereas returning empty hash table actually returns an empty hash table:

PS C:\> $ret = Invoke-Command -ScriptBlock {return @{}}  
PS C:\> $ret.GetType()  
  
IsPublic IsSerial Name                                     BaseType  
-------- -------- ----                                     --------  
True     True     Hashtable                                System.Object  

Let's assume I have no control over what is inside the script block (although I could inject code in the beginning of the script block), I just need to act on the return value which could be of any type (array, hash table, integer, string, boolean etc.). Is there a way from outside the script block to find out if the returned value really was null or @()?

Thanks!

Windows Server PowerShell
Windows Server PowerShell
Windows Server: A family of Microsoft server operating systems that support enterprise-level management, data storage, applications, and communications.PowerShell: A family of Microsoft task automation and configuration management frameworks consisting of a command-line shell and associated scripting language.
5,364 questions
0 comments No comments
{count} votes

Accepted answer
  1. Rich Matheisen 44,776 Reputation points
    2020-08-29T20:07:18.677+00:00

    The use of "return" in PowerShell is there to break out of a function somewhere other than the end. Doing a 'return $var' is the same as doing '$var | Write-Output; return'. So, you can see what's happening. PowerShell doesn't just return what follows the 'return', it shoves all uncaptured output into the pipeline.

    From a PowerShell prompt, if you were to do this: "@()" you'd get nothing. If you did "@(1,2)" you'd get two lines of output (one for each element of the array).

    What's happening is that the variable $var is being fed into the pipeline, it's not being returned as a single object of type [array].

    Trying to "fix" this isn't going to work because this is the way PowerShell works, it's not 'C', 'Pascal", 'C++', 'C#', 'Python', etc. Whether it's a bug or a feature depends on your (or your script writer's) understanding of the language. If you want an array to always be treated as an array, try using something like this:

    function flat{
        $arr = @(1)
        if ($arr.length -lt 2){
            ,$arr
        }
        else{
            $arr
        }
    }
    

1 additional answer

Sort by: Most helpful
  1. MotoX80 31,571 Reputation points
    2020-08-29T12:49:39.56+00:00

    Can't you just test for null?

    $ret = Invoke-Command -ScriptBlock {return @()} 
     if ($ret -eq $null) {
        "its null"
     } else {
        $ret.GetType()
     }