manski's blog

PowerShell functions for the uninitiated (C# programmer)

Being a C# programmer, I recently found some use for Microsoft’s PowerShell (the cmd replacement). What’s nice about PowerShell is that it has full access to the .NET framework.

However, there are also some very pit falls when coming from C# (or any related programming language).

There’s one very mean pit fall when it comes to functions and their return values that – if you don’t exactly know how PowerShell works – makes you pull out your hair.

Assume this PowerShell code:

function PrintAndReturnSomething {
  echo "Hello, World"
  return 42

$result = PrintAndReturnSomething

What do you expect $result to be? An integer with value 42, right? Let’s check it out.

We’re adding the following line to the end of the script:

echo ("Return type: " + $result.GetType().FullName)

Now, when you run the script, you’ll get:

Return type: System.Object[]

Wait, what? Where’s the “Hello, World”? And why the heck is $return an object array?

The first problem (pit fall) is that PowerShell treats every non-captured object (i.e. one that isn’t assigned to a variable) as return value.

Functions Return Everything That Isn’t Captured

Let’s have a look at a different script for a moment:

function LongNumericString {
  $strBld = new-object System.Text.StringBuilder
  for ($i=0; $i -lt 20; $i++) {
  return $strBld.ToString()

One would expect that LongNumericString returns just a string; but it doesn’t. Instead it returns an object[] with this contents:

Capacity           MaxCapacity                        Length
——–                ———–                               ——
16                 2147483647                         1
16                 2147483647                         2
16                 2147483647                         3
16                 2147483647                         4
16                 2147483647                         5
16                 2147483647                         6
16                 2147483647                         7
16                 2147483647                         8
16                 2147483647                         9
16                 2147483647                        10
16                 2147483647                        12
16                 2147483647                        14
16                 2147483647                        16
32                 2147483647                        18
32                 2147483647                        20
32                 2147483647                        22
32                 2147483647                        24
32                 2147483647                        26
32                 2147483647                        28
32                 2147483647                        30

The problem here is that $strBld.Append returns a StringBuilder object. And since this return value isn’t assigned to a variable, PowerShell considers it part of the return value.

To resolve this problem, prefix $strBld.Append with [void]:

function LongNumericString {
  $strBld = new-object System.Text.StringBuilder
  for ($i=0; $i -lt 20; $i++) {
  return $strBld.ToString()

Note: The keyword return is totally optional. The expression return $strBld.ToString() is equivalent to $strBld.ToString(), and even to $strBld.ToString(); return.

Function Return Values = Function Output

Back to our initial script. Now that we know how PowerShell composes return values for functions, why didn’t PrintAndReturnSomething return just 42?

One needs to understand that PowerShell doesn’t actually care about return value but rather about output of functions. The return value is just considered to be a part of the output. This way, PowerShell doesn’t just allow you to pipe text (like ls -l | sort in bash), but to pipe actual objects.

For example, Get-ChildItem C:\ | Format-Table Name, Length is the same as:

$child_items = Get-ChildItem C:\
Format-Table -InputObject $child_items Name, Length

PrintAndReturnSomething uses echo (which is an alias for Write-Output) to print “Hello, World”. This is output and thus part of the function’s return value.

To force PowerShell to write something to the console (rather than including it in the return value), use Write-Host instead of echo.

The corrected code is:

function PrintAndReturnSomething {
  Write-Host "Hello, World"
  return 42

$result = PrintAndReturnSomething
# Return is now a "System.Int32" with value 42.


To sum things up:

  • PowerShell functions will always return (as object[], if there’s more than one return value):

    • all uncaptured objects (i.e. objects that haven’t been assigned to variables)
    • as well as all output (from echo/Write-Output)
  • Exclude from return value:

    • Uncaptured objects: prefix with [void]
    • Console output: use Write-Host instead of echo/Write-Output


  1. George said: ∞

    I am pulling my hair out trying to get an exit code out of a simple function I’ve written. Your explanation helps me understand WHY my function is not working as I want it to, but I can’t see a way out of this mess. My function uses Start-Process to call any arbitrary .exe. The exe may write to the console and thus generate arbitrary values which are returned. I capture the exit code from the exe and want to pass that back to the caller, but it, along with an arbitrarily long array of console output are returned. There doesn’t seem to be a predictable way to parse for just my return code. Like in your example, I want to capture that 42. Indeed the answer to life, the universe, and everything else!

    • Sebastian Krysmanski (post author) replied: ∞

      If you store the return value of Start-Process in a variable, does this contain the output of the process? Or just its return value? I’ve not tried this situation (and am fairly new to PowerShell).

  2. Bernado Nguyen-Hoan said: ∞

    Thanks for posting this! I was indeed pulling my hair out as to why the function was returning an array!

  3. Pablo said: ∞

    A million thanks for this excellent explanation.
    This explains why my function was returning crazy results!

  4. Chris said: ∞

    $LASTEXITCODE in conjunction with $? variable or place .exe call in try catch and throw $_ which will halt the script and return error. $error variable is another alternative.

    Recommended books: powershell in depth and powershell in action.

  5. Chuck Staples said: ∞

    Nice explanations for an irritating PS-specific problem.

  6. Deepak said: ∞


Leave a Reply

Your email address will not be published. Required fields are marked *