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 about_Return page on TechNet, or by invoking the help return command from PowerShell itself.
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 about_Return page on TechNet, or by invoking the help return command from PowerShell itself.
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.
Powershell function return value
Explaining Rationale for Function Output Behavior
Why not use the return statement?
Functions that return arrays
When you chain cmdlets together, the PowerShell pipeline wants/needs/is designed to feed "one thing at a time" down the pipeline, e.g.
Get-Content 'somefile' | Process-TextLine Get-ChildItem | Process-Files
It doesn't matter what you do inside the function with @() and [array], it's the connection between functions where PowerShell will undo that and treat them as individual items again.
Which is usually what you want - it means you can use the pipeline, and | foreach-object {} and foreach ($thing in ____) {}, and if you just assign the results to a variable and there were multiple results, it will be an array.
The two main reasons you might want to force an array are:
-
The case the file was empty and you're getting
$nullbut want an empty array. This also normally works fine for pipeline and loops, but storing in a variable doesn't work. That's your@()and[array]in the outer, calling, function where you store the results. This is (AFAIK) good practice to do it like this and not try to do it from inside the function. -
The case where you want to group things together and send (3 lines) down the pipeline all together.
e.g.
PS D:\> @(1,2,3),@(4,5,6) | foreach { "-$_-" }
-1 2 3-
-4 5 6-And when there is only one of those, it falls over:
@(1,2,3) | ForEach { "-$_-" }
-1-
-2-
-3-This is the case where you need u/TalkToTheFinger's solution of putting a comma in front, and all this does is force it to be like an array inside an array:
PS D:\> @(,@(1,2,3)) | ForEach { "-$_-" }
-1 2 3-
It looks a bit weird, like you should be able to write @(@(1,2,3)) or @(@(1,2,3), ) or @($null, @(1,2,3)) but they are all not quite the same, to get it right and make it work needs one comma in front.
The above behaviour still happens - the array is unpacked and each item is sent down the pipeline, it's just that each item is now "an array of 3 items".
There are two cases here - if you want [array]$thing = get-content 'filename' to force an array even if there's only one or no lines, do it that way.
If you try to force it with the comma, it will break in the normal case of lots of lines and they will all be sent together. i.e. it works to force an array in the case of 0 or 1 result, but it breaks in the normal case of lots of results - instead of "an array of results" you have "an array of array of results". If you want to write your own get-thing and you try to force it with ,$lines what you'll get is "an array of (an array of lines)" and that will be silly.
But sometimes you do want that "several arrays together, don't flatten them" behaviour, you can force it, and they will stay together down through the pipeline.
More on reddit.comVideos
Coming from a managed code background, I use return in just about every function I write. When I want to return a collection to the pipeline, I add a # return comment so I know that I explicitly meant to return the object. For the community and my coworkers... I am on the wrong side of this battle. I'll continually receive code reviews saying I should not use the return statement, but I think thats their choice. I use it for the purpose of being explicit. I've helped the same people who want me to stop using it debug their broken code because they accidentally were returning something else into their pipeline that they didn't want there. Not just once, but at least once a sprint. Each.
So, grand PowerShell community, what's up with the pushback on return?