PowerShellPracticeAndStyle
PowerShellPracticeAndStyle copied to clipboard
Write-Output vs. Return vs. $Output
There is currently a recommendation in Function and Structure to not use "return" but it says to "just put the variable on a line by itself" ...
I wonder if that's the majority opinion?
Assuming this is the last line of my function, what's better:
$output = $temp + (Get-Thing $temp)
return $output
$output = $temp + (Get-Thing $temp)
$output
$output = $temp + (Get-Thing $temp)
Write-Output $output
I won't go into piping output to Write-Output ;-)
I try to use Write-Output when logical.
Personally, I tend to use the return statement in functions where I only intend to output a single object (or at least only output from that line.) I know that the engine doesn't guarantee that, but it's just my habit, and it makes the code easier for me to read later on.
In exported, cmdlet-style functions, I tend to just put commands or variables on their own line and let it send stuff to the output stream. (This is mostly done in a Process block and inside a foreach loop, where multiple objects can wind up output.)
I never use return except to return early and I never use Write-Output I just specify the variable on a line by itself like the OP's middle example. I'm not too opinionated on whether or not to specify output on the same line as the return in the early return case.
Personally I wish the language would have never allowed you to specify an expression as part of a return statement. I think that would have eliminated some confusion over how PowerShell functions work. But that ship has sailed.
I never use return or Write-Output. I tend to just let the engine output whatever isn't captured. And whenever possible I try to stream objects out one at a time - that's an important point when writing pipeline capable functions.
Ditto.
I would use return religiously if it was contractual, but since it is not I don't use it.
IMHO, both return and Write-Output give false expectations to someone less familiar to PowerShell.
-----Original Message----- From: "mattmcnabb" [email protected] Sent: 2015-08-15 12:08 PM To: "PoshCode/PowerShellPracticeAndStyle" [email protected] Subject: Re: [PowerShellPracticeAndStyle] Write-Output vs. Return vs. $Output(#46)
I never use return or Write-Output. I tend to just let the engine output whatever isn't captured. And whenever possible I try to stream objects out one at a time - that's an important point when writing pipeline capable functions. — Reply to this email directly or view it on GitHub.
I like Write-Output. I have my own rule of always explicitly sending an object to a stream. It makes it easier to understand at a glance in the future.
Return should be contractual, but it's not. It's useful for returning early, but other than that I don't think it should be used.
Letting objects fall out of the pipe is probably most familiar to shell users, and doesn't carry the implicit expectation that it ends program output, but to me it's less clear.
Ultimately I think Write-Output is clearer and better to someone familiar with PowerShell, and I think we should ultimately be writing for them.
Some thoughts.
In theory, or in rare practical cases, a user may redefine Write-Output for
some reasons, i.e. debugging, logging, or even accidentally. This may affect
the code which uses Write-Output naked, without specifying its full name with
its original module. This may or may not be what a user wants.
Note that user aliases and functions have higher precedence than cmdlets, see help about_Command_Precedence.
On the other hand, a user cannot alter explicit return or implicit output
statements.
So naked Write-Output is somewhat more flexible and at the same time less safe.
Write-Output performance is probably worse than return or output. If a
function is called from a loop this may be an issue.
From a few experiments, it appears to me that using Write-Output results in worse perf but like you say, that's probably only a concern when executed from a loop with lots of iterations.
BTW I agree with @KirkMunro that using Write-Output sets up false expectations that that is the only way to write output which simply isn't true.
Also, would you write code like this:
for ($i=0; $i -lt 10000; $i++) { $sum += Write-Output $i }
I wouldn't but technically you are writing $i to the output of the pipeline that's on the RHS of the assignment statement.
Why should we not use return or write-output, cannot find that information. Does it not return to the output stream / pipeline? I have never experienced a problem with return, but i almost never pipe my functions to something else. Thanks
@maekee to sum up this long thread:
In other programming languages with a return statement, the return statement is contractual: nothing returns output from a function except the return statement. Thus, for people with experience in other languages, the presence of code like return $output in PowerShell is misleading. They think: "oh, ok, PowerShell has a return statement" and don't realize that any un-captured result values get output also -- and don't even realize they can output multiple times.
The reasoning for, and objection to, Write-Output is similar. People like it because:
... always explicitly sending an object to a stream... makes it easier to understand [the code] at a glance in the future.
The problem is that this can lead to a false sense of security. If you see Write-Output you assume that output can only come from lines with Write-Output, when in reality it can come from any command or method call. Or at least, any that doesn't start with an assignment (or [void] cast), or end with out-null ...
So while some people like it because it highlights the spots where you intentionally output something -- other people argue it's presence distracts you from the fact that other lines could output.
Therefore
Since return cannot always be used (sometimes you need to write output more than once), you should prefer Write-Output and use return only for ending execution. But since Write-Output can lead to false expectations and in addition is slightly slower, (and worse, can be overwritten by functions or aliases) ...
We recommend:
Use return only for ending execution.
Avoid Write-Output. Instead, when you want to make output clearer, just assign output to a relevantly named variable. and put that a variable on a line by itself to signal explicit output. In particular, avoid Write-Output -NoEnumerate which is broken in PowerShell 6.
Thanks for taking your time to explain. So if i want to end execution, i only write return and nothing more?
/Micke
2 aug. 2016 kl. 00:30 skrev Joel Bennett [email protected]:
@maekee to sum up this long thread:
In other programming languages with a return statement, the return statement is contractual: nothing returns output from a function except the return statement. Thus, for people with experience in other languages, the presence of code like return $output in PowerShell is misleading. They think: "oh, ok, PowerShell has a return statement" and don't realize that any un-captured result values get output also -- and don't even realize they can output multiple times.
The reasoning for, and objection to, Write-Output is similar. People like it because:
... always explicitly sending an object to a stream... makes it easier to understand [the code] at a glance in the future.
The problem is that this can lead to a false sense of security. If you see Write-Output you assume that output can only come from lines with Write-Output, when in reality it can come from any command or method call. Or at least, any that doesn't start with an assignment (or [void] cast), or end with out-null ...
So while some people like it because it highlights the spots where you intentionally output something -- other people argue it's presence distracts you from the fact that other lines could output.
Therefore
Since return cannot always be used (sometimes you need to write output more than once), you should prefer Write-Output and use return only for ending execution. But since Write-Output can lead to false expectations and in addition is slightly slower, (and worse, can be overwritten by functions or aliases) ...
We recommend:
Use return only for ending execution.
Avoid Write-Output (although you may want to use for it's -NoEnumerate switch). Instead, when you want to make output clearer, just assign output to a relevantly named variable. and put that a variable on a line by itself to signal explicit output.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
@maekee, in PowerShell the flow control keywords of break, continue, and return should really only be used to control execution.
Continue moves to the next item in a loop and stops processing the current one.
Break pops us out of the loop entirely.
Return stops the current block (begin/process/end) block execution and returns to the caller (or the prompt).
Awesome, thanks. So i van use Return without any "parameter"? Or is break better for that?
/Micke
2 aug. 2016 kl. 14:31 skrev Stephen Owen [email protected]:
@maekee, in PowerShell the flow control keywords of break, continue, and return should really only be used to control execution.
Continue moves to the next item in a loop and stops processing the current one.
Break pops us out of the loop entirely.
Return stops all execution and dumps us back to the console.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
You can't (shouldn't) use break or continue outside of a loop construct like foreach/for/do/while/switch -- they are for loops exclusively.
This discussion was just hitting its stride and ended. Was there ever a final (agreeable) verdict or consensus?
See my summary with it's 8 up-votes?
We just need to capture this in the docs and close it.
TIL Write-Output -NoEnumerate was broken on PowerShell 6 for 16 months or longer. In summary:
DO NOT USE Write-Output -- EVER.
@Jaykul : you might want to edit https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/46#issuecomment-236727676 and stripe out the -NoEnumerate bit there.
Ugh. What I should do, is copy that into the book somewhere and close this thread. It's always next weekend...
This times 1000x.
The Write-Output -NoEnumerate issue is fixed in PS6.2.0 and up. 🎉
@vexx32 Write-Output -NoEnumerate is not fully fixed in PS6.2.0 and up:
See: https://github.com/PowerShell/PowerShell/issues/5122
PS C:\Users\john.zabroski> ($PSVersionTable).PSVersion
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
6 2 1
PS C:\Users\john.zabroski> (Write-Output -NoEnumerate 1).GetType().FullName
System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
PS C:\Users\john.zabroski> (1 | Write-Output -NoEnumerate).GetType().FullName
System.Int32
and also:
PS C:\Users\john.zabroski> (Write-Output -NoEnumerate @()).GetType().FullName
System.Object[]
The problem still exists for:
- Direct argument
- Not a collection
The problem is fixed for:
- Pipeline argument
- Collection or scalar value
Interesting. Mind xposting that to the PS repo? We should fix that too ^^
Though I don't see anything wrong with that last one?
The last one is correct. Just provided for completeness.
It seems astonishing to me as a C coder that you can't just say 'return $something' and have $something returned.
I've been brushing up on PS recently and decided to use it to write a fairly complex custom backup system with numerous functions returning objects and values, and I find myself in a continuous miasma of 'Why is that value appearing in the terminal?' and 'Why is that being returned?', and have been through various iterations of using return, then using Write-Output, then just having the variable there with a return statement on the next line, and all seem to be very much doing what they fancy rather than what I want them to.
This idea of just plonking a variable name in with no assignment and it is left for something else to pick up is I guess great for pipelining but IMO it makes PS too flaky to use for anything more than little utility scripts - which is fair enough I guess, that's what PS is for and if I wasn't using this exercise to learn what's new and changed in PS I'd have written it in a more suitable language, but it really puts me off using PS for those tasks where you want a bit of complexity but want to keep things fairly simple, and a scripting language where you can get at .Net is a great concept.
Could there not be a 'Plonk' statement where you say 'Plonk $something' and it does the same as this curious lonely '$something' statement, with backwards compatibility so the lonely variable still worked but at least an explicit 'Plonk' would be clear. Of course, it's clear to experienced PS users, but it makes it rather frustrating for even a seasoned programmer of C or C# or the like to get something vaguely complex working.
Surely there can be an implementation of 'return $something' that does what programmers think it does.
Maybe with some pragmas or flags or whatever to make the intention explicit but the net seems to be full of confusion about something so basic as how to get a function to just return an object or variable to the caller, which surely can't be healthy for a language !
I vaguely considered just having a global $TheThingThatWasReturned and making all functions use that, it's single-threaded and only for home use so what the hell, it's a code smell as strong as a vat of last year's Stilton but at least I'd know what was going on :)
Miasma. Perfect description
Could there not be a 'Plonk' statement where you say 'Plonk $something' and it does the same as this curious lonely '$something' statement, with backwards compatibility so the lonely variable still worked but at least an explicit 'Plonk' would be clear. Of course, it's clear to experienced PS users, but it makes it rather frustrating for even a seasoned programmer of C or C# or the like to get something vaguely complex working.
Write-Output does this.
Write-Output does this.
Write-Output is broken across PowerShell versions, which is the whole point of this issue.
@silentdiverchris In my experience, PowerShell is the epitome of Blub and PowerShell users literally don't feel or see the pain you or I experience when using it. It's best to point out things that don't actually work properly, like Write-Output -NoEnumerate, because otherwise it becomes a religious discussion and gets nowhere.
That does work now, though, unless you're adamant about using an already out-of-support version of pwsh. I don't think any versions still in support have that bug.
Also, using -NoEnumerate at all is a bit of an edge case. Passing whole collections down the pipeline is generally inadvisable, PowerShell doesn't handle it that well anyway.
unless you're adamant about using an already out-of-support version of pwsh.
Our IT department uses PowerShell 5.0 for a lot of things, in part because of third party dependencies and its a small team.
Anyway, Write-Output -NoEnumerate is just an example of documenting that something doesnt work correctly, and staying out of religious discussions like 'C does this differently and C's approach seems better for complex, systems programming'. I will say, if you are writing a backup utility, @silentdiverchris , you should use C# and use the open source VSS library available on nuget.org - but that also requires becoming very familiar with admining VSS