functionpowershellpowershell-7.0

What decides if a value is returned from a PowerShell function?


I'm trying to figure out what dictates if a value is returned from a PowerShell function or not, and I've run into some oddities. The about_return docs say:

In PowerShell, the results of each statement are returned as output, even without a statement that contains the Return keyword.

But this seems to glaze over details. If I run this:

function My-Function {
    1
    [System.Console]::WriteLine("Hello")
    $null
    $true
    $false
    0
    2
}

Running this returns an array of (along with printing "Hello"):

1
True
False
0
2

Which means that $null isn't auto-returned. Then I tried incrementing since I'm doing using that in a function:

function My-Function {
    $n = 1
    $n
    $n++
    ($n++)
    -join @(1, 2, 3)
    (-join @(1, 2, 3))
}

Returns:

1
2
123
123

So, $n and $n++ were returned, but ($n++) wasn't? But then when compared to another operator (-join), it's the same in each case. Why does wrapping parenthesis around $n++ prevent it from being returned, and why don't other operators behave the same? This is even more confusing since the = operator appears to work in the opposite way:

function My-Function {
    ($n = 1)
    $n
    $n++
    ($n++)
}

Returns:

1
1
2

Now wrapping the assignment causes it to be returned, whereas wrapping $n++ causes it to not be returned.

In summary, I'd just like to know a straightforward way of being able to look at a line of code in a function, and determine if it will cause a value to be returned or not.


Solution

  • This section discusses the specific statements from your sample functions.
    See the next section for background information.


    PowerShell's output ("return value") behavior:

    Tip of the hat to iRon for his help.

    PowerShell, following the model of traditional shells, is organized around streams - see the conceptual about_Redirection help topic for an overview of all 6 streams that PowerShell supports.[1]

    That is, any statement - and therefore potentially multiple ones - in a script or function can write to any of the output streams.

    The primary output stream, meant to convey data, is the success output stream (whose number is 1), and only it is sent through the pipeline by default, and therefore by default only it is captured in a variable, suppressed, or redirected to a file.

    There are two ways to write to the success output stream, i.e. to produce data output:

    As for assignments - e.g. $n = 1; $n += 1; ++$n; $n--:

    As for output enumeration behavior:

    As for outputting $null:

    As for suppressing (discarding) unwanted output / redirecting to a file:

    As for merging output streams:

    As for bypassing PowerShell's system of streams:

    As for how output is formatted:

    As for how the outside world sees PowerShell's output streams:


    [1] Note that PowerShell has no concept of an input stream as such, and therefore also does not support the stdin redirection operator < familiar from other shells. Instead, commands receive streaming input (only) via the pipeline. In order to receive data from the outside world, via the PowerShell CLI's stdin stream, the automatic $input variable must be used - see this answer.

    [2] Using > (or >>) to redirect to a file effectively uses the Out-File cmdlet behind the scenes, and therefore its default character encoding, which is "Unicode" (UTF-16LE) in Windows PowerShell, and BOM-less UTF-8 in PowerShell (Core) 7+. However, in PowerShell version 5.1 and above you can control this encoding via the $PSDefaultParameterValues preference variable - see this answer.