Function return value in PowerShell

Powershell

Powershell Problem Overview


I have developed a PowerShell function that performs a number of actions involving provisioning SharePoint Team sites. Ultimately, I want the function to return the URL of the provisioned site as a String so at the end of my function I have the following code:

$rs = $url.ToString();
return $rs;

The code that calls this function looks like:

$returnURL = MyFunction -param 1 ...

So I am expecting a String, however it's not. Instead, it is an object of type System.Management.Automation.PSMethod. Why is it returning that type instead of a String type?

Powershell Solutions


Solution 1 - Powershell

PowerShell has really wacky return semantics - at least when viewed from a more traditional programming perspective. There are two main ideas to wrap your head around:

  • All output is captured, and returned
  • The return keyword really just indicates a logical exit point

Thus, the following two script blocks will do effectively the exact same thing:

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

The $a variable in the second example is left as output on the pipeline and, as mentioned, all output is returned. In fact, in the second example you could omit the return entirely and you would get the same behavior (the return would be implied as the function naturally completes and exits).

Without more of your function definition I can't say why you are getting a PSMethod object. My guess is that you probably have something a few lines up that is not being captured and is being placed on the output pipeline.

It is also worth noting that you probably don't need those semicolons - unless you are nesting multiple expressions on a single line.

You can read more about the return semantics on the http://technet.microsoft.com/en-us/library/hh847760.aspx">about_Return</a> page on TechNet, or by invoking the help return command from PowerShell itself.

Solution 2 - Powershell

This part of PowerShell is probably the most stupid aspect. Any extraneous output generated during a function will pollute the result. Sometimes there isn't any output, and then under some conditions there is some other unplanned output, in addition to your planned return value.

So, I remove the assignment from the original function call, so the output ends up on the screen, and then step through until something I didn't plan for pops out in the debugger window (using the PowerShell ISE).

Even things like reserving variables in outer scopes cause output, like [boolean]$isEnabled which will annoyingly spit a False out unless you make it [boolean]$isEnabled = $false.

Another good one is $someCollection.Add("thing") which spits out the new collection count.

Solution 3 - Powershell

With PowerShell 5 we now have the ability to create classes. Change your function into a class, and return will only return the object immediately preceding it. Here is a real simple example.

class test_class {
    [int]return_what() {
        Write-Output "Hello, World!"
        return 808979
    }
}
$tc = New-Object -TypeName test_class
$tc.return_what()

If this was a function the expected output would be

Hello World
808979

but as a class the only thing returned is the integer 808979. A class is sort of like a guarantee that it will only return the type declared or void.

Solution 4 - Powershell

As a workaround I've been returning the last object in the array that you get back from the function... It is not a great solution, but it's better than nothing:

someFunction {
   $a = "hello"
   "Function is running"
   return $a
}

$b = someFunction
$b = $b[($b.count - 1)]  # Or
$b = $b[-1]              # Simpler

All in all, a more one-lineish way of writing the same thing could be:

$b = (someFunction $someParameter $andAnotherOne)[-1]

Solution 5 - Powershell

I pass around a simple Hashtable object with a single result member to avoid the return craziness as I also want to output to the console. It acts through pass by reference.

function sample-loop($returnObj) {
  for($i = 0; $i -lt 10; $i++) {
    Write-Host "loop counter: $i"
    $returnObj.result++
  }
}

function main-sample() {
  $countObj = @{ result = 0 }
  sample-loop -returnObj $countObj
  Write-Host "_____________"
  Write-Host "Total = " ($countObj.result)
}

main-sample

You can see real example usage at my GitHub project unpackTunes.

Solution 6 - Powershell

The existing answers are correct, but sometimes you aren't actually returning something explicitly with a Write-Output or a return, yet there is some mystery value in the function results. This could be the output of a builtin function like New-Item

PS C:\temp> function ContrivedFolderMakerFunction {
>>    $folderName = [DateTime]::Now.ToFileTime()
>>    $folderPath = Join-Path -Path . -ChildPath $folderName
>>    New-Item -Path $folderPath -ItemType Directory
>>    return $true
>> }
PS C:\temp> $result = ContrivedFolderMakerFunction
PS C:\temp> $result


    Directory: C:\temp


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         2/9/2020   4:32 PM                132257575335253136
True

All that extra noise of the directory creation is being collected and emitted in the output. The easy way to mitigate this is to add | Out-Null to the end of the New-Item statement, or you can assign the result to a variable and just not use that variable. It would look like this...

PS C:\temp> function ContrivedFolderMakerFunction {
>>    $folderName = [DateTime]::Now.ToFileTime()
>>    $folderPath = Join-Path -Path . -ChildPath $folderName
>>    New-Item -Path $folderPath -ItemType Directory | Out-Null
>>    # -or-
>>    $throwaway = New-Item -Path $folderPath -ItemType Directory 
>>    return $true
>> }
PS C:\temp> $result = ContrivedFolderMakerFunction
PS C:\temp> $result
True

New-Item is probably the more famous of these, but others include all of the StringBuilder.Append*() methods, as well as the SqlDataAdapter.Fill() method.

Solution 7 - Powershell

It's hard to say without looking at at code. Make sure your function doesn't return more than one object and that you capture any results made from other calls. What do you get for:

@($returnURL).count

Anyway, two suggestions:

Cast the object to string:

...
return [string]$rs

Or just enclose it in double quotes, same as above but shorter to type:

...
return "$rs"

Solution 8 - Powershell

Luke's description of the function results in these scenarios seems to be right on. I only wish to understand the root cause and the PowerShell product team would do something about the behavior. It seems to be quite common and has cost me too much debugging time.

To get around this issue I've been using global variables rather than returning and using the value from the function call.

Here's another question on the use of global variables: https://stackoverflow.com/questions/12535419/setting-a-global-powershell-variable-from-a-function-where-the-global-variable-n

Solution 9 - Powershell

The following simply returns 4 as an answer. When you replace the add expressions for strings it returns the first string.

Function StartingMain {
  $a = 1 + 3
  $b = 2 + 5
  $c = 3 + 7
  Return $a
}

Function StartingEnd($b) {
  Write-Host $b
}

StartingEnd(StartingMain)

This can also be done for an array. The example below will return "Text 2"

Function StartingMain {
  $a = ,@("Text 1","Text 2","Text 3")
  Return $a
}

Function StartingEnd($b) {
  Write-Host $b[1]
}

StartingEnd(StartingMain)

Note that you have to call the function below the function itself. Otherwise, the first time it runs it will return an error that it doesn't know what "StartingMain" is.

Solution 10 - Powershell

You need to clear output before returning. Try using Out-Null. That's how powershell return works. It returns not the variable you wanted, but output of your whole function. So your example would be:

function Return-Url
{
   param([string] $url)

   . {
       $rs = $url.ToString();
       return
   } | Out-Null
   
   return $rs
}

$result = Return-Url -url "https://stackoverflow.com/questions/10286164/function-return-value-in-powershell"

Write-Host $result
Write-Host $result.GetType()

And result is:

https://stackoverflow.com/questions/10286164/function-return-value-in-powershell
System.String

Credits to https://riptutorial.com/powershell/example/27037/how-to-work-with-functions-returns

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChiliYagoView Question on Stackoverflow
Solution 1 - PowershellGoyuixView Answer on Stackoverflow
Solution 2 - PowershellLuke PuplettView Answer on Stackoverflow
Solution 3 - PowershellJohn ChalkerView Answer on Stackoverflow
Solution 4 - PowershellJonesyView Answer on Stackoverflow
Solution 5 - PowershellWhat Would Be CoolView Answer on Stackoverflow
Solution 6 - PowershellStingyJackView Answer on Stackoverflow
Solution 7 - PowershellShay LevyView Answer on Stackoverflow
Solution 8 - PowershellTed B.View Answer on Stackoverflow
Solution 9 - PowershellEdwinView Answer on Stackoverflow
Solution 10 - Powershell3lvinazView Answer on Stackoverflow