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.
This section discusses the specific statements from your sample functions.
See the bottom section for background information.
$n = 1
and $n++
are assignments and therefore do not produce output. $n
is an expression whose value is output $null
- ditto, but even though it is output , it doesn't display by default ($n++)
- due to enclosure in (...)
- turns the assignment into an expression and therefore does output the assigned value (too).
(++$n)
[System.Console]::WriteLine("Hello")
prints directly to the console , which bypasses PowerShell's system of output streams.
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, ie to produce data output :
Explicitly , with a Write-Output
call - although that is rarely needed .
Typically implicitly , by neither capturing, suppressing, nor redirecting output produced by a statement .
In other words: Output from any command (eg, Get-ChildItem *.txt
) or expression (eg, 1 + 2
) is sent to the success output stream by default .
Unlike in traditional programming languages, return
is not needed to produce output - in fact, its primary purpose is to exit the enclosing scope independently of any output the scope produces, though as a syntactic convenience you can combine the two aspects:
return <command-or-expression>
is in effect the same as the following two statements, the first of which (potentially) produces output, the second of which exits the scope: <command-or-expression>; return
<command-or-expression>; return
As for assignments - eg $n = 1; $n += 1; ++$n; $n--
$n = 1; $n += 1; ++$n; $n--
$n = 1; $n += 1; ++$n; $n--
:
(...)
, the grouping operator ; eg ($n = 1)
both assigns 1
to variable $n
and outputs 1
, which allows it to participate in larger expressions, such as ($n = 1) -gt 0
$(...)
( subexpression operator ) and @(...)
( array-subexpression operator ) do not have that effect - they wrap one or more entire statement(s) , without affecting the enclosed statements' intrinsic output behavior; eg $($n = 1)
does not produce output, because $n = 1
by itself doesn't produce output; however, $(($n = 1))
does , because ($n = 1)
by itself does.As for output enumeration behavior :
By default, PowerShell enumerates collections that are being output, in the spirit of streaming output: That is, it sends a collection's elements to the pipeline, one by one .
In the rare event that you do need to output a collection as a whole - which in general should be avoided, so as not to confound other commands participating in a pipeline, which usually do expect object-by-object input - you have two options:
, $collection
(sic; uses an aux. one-element wrapper array) Write-Output -NoEnumerate $collection
As for outputting $null
:
$null
is output to the pipeline , but by default doesn't show .
$null
by itself produces no visible output,
but the following returns $true
, demonstrating that the value was sent:
$null | ForEach-Object { $null -eq $_ } # -> $true
Note that PowerShell also has an "array-valued $null
" value that it uses to represent the lack of output from a command, which is technically represented as the [System.Management.Automation.Internal.AutomationNull]::Value
singleton. In expression contexts, this values is treated the same as $null
, but in the pipeline it behaves like an enumerable without elements and therefore sends nothing through the pipeline - see this answer for more information.
As for suppressing (discarding) unwanted output / redirecting to a file :
The best general-purpose way to suppress a statement's success output is to assign to $null
( $null = ...
); eg:
# .Add() outputs a value, which is usually unwanted. $null = ($list = [System.Collections.ArrayList]::new()).Add('hi')
Note: The following discusses output suppression , via $null
as the redirection target, but applies analogously to redirecting output to a file , by specifying a file name or path as the target. [2]
To selectively suppress a different output stream, prefix >$null
with its number ; eg 3>$null
suppresses warning stream output.
To suppress output from all streams , which in the case of external programs covers both stdout and stderr, use redirection *>$null
.
As for merging output streams :
1
) can be merged into .2>&1
and/or 3>&1
), or merge all (others) : *>&1
2
) are [System.Management.Automation.ErrorRecord]
instances - see this answer for more information.As for bypassing PowerShell's system of streams:
Out-Host
and [Console]::WriteLine()
calls bypass PowerShell's output streams and write directly to the host / console (terminal). (A host is any environment that hosts the PowerShell engine, which is typically , but not necessarily a console (terminal); examples of other hosts are the PowerShell SDK and the host used in PowerShell remoting ).
Write-Host
used to unconditionally bypass PowerShell's output streams and still does by default , but - since PowerShell version 5 - now sends its output to the information stream (stream number 6
), where it can be captured / redirected on demand - see this answer for more information.
[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 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.