Why is this needed?
It is needed, because the BorderAround
method has a return value and, in PowerShell, any command or expression that outputs (returns) data is implicitly output to the (success) output stream, which by default goes to the host, which is typically the console window (terminal) in which a PowerShell session runs.
That is, the data shows in the console/terminal, unless it is:
- captured (
$var = ...
)
- sent through the pipeline for further processing (
... | ...
; the last pipeline segment's command may or may not produce output itself)
- redirected (
... >
)
or any combination thereof.
That is:
$range.BorderAround(1, -4138)
is (more efficient) shorthand for:
Write-Output $range.BorderAround(1, -4138)
(Explicit use of Write-Output
is rarely needed.)
Since you don't want that output, you must suppress it, for which you have several options:
$null = ...
[void] (...)
... > $null
... | Out-Null
$null = ...
may be the best overall choice, because:
It conveys the intent to suppress up front
- While
[void] = (...)
does that too, it often requires you to enclose an expression in (...)
for syntactic reasons; e.g., [void] 1 + 2
doesn't work as intended, only [void] (1 + 2)
; similarly, a command must always be enclosed in (...)
; [void] New-Item test.txt
doesn't work, only [void] (New-Item test.txt)
does.
It performs well with both command output (e.g., $null = Get-AdUser ...
) and expression output (e.g., $null = $range.BorderAround(1, -4138)
).
Conversely, avoid ... | Out-Null
, because it is generally much slower (except in the edge case of a side effect-free expression's output in PowerShell (Core) 6+)[1].
However, if you need to silence all output streams - not just the success output, but also errors, verbose output, ... - you must use *> $null
Why does PowerShell produce output implicitly?
As a shell, PowerShell's output behavior is based on streams, as in traditional shells such as cmd.exe
or Bash. (While traditional shells have 2 output streams - stdout and stderr - PowerShell has 6, so as to provide more sophisticated functionality - see about_Redirection.)
A cmdlet, script, or function can write to the output streams as often as it wants, and such output is usually instantly available for display but notably also to potential consumers, which enables the streaming, one-by-one processing that the pipeline provides.
This contrasts with traditional programming languages, whose output behavior is based on return values, typically provided via the return
keyword, which conflates output data (the return value) with flow control (exit the scope and return to the caller).
- A frequent pitfall is to expect PowerShell's
return
statement to act the same, but it doesn't: return <val>
is just syntactic sugar for <val>; return
, i.e., implicit output of <val>
followed by an unconditional return of control to the caller; notably, the use of return
does not preclude generation of output from earlier statements in the same scope.
Unlike traditional shells, PowerShell doesn't require an explicit write-to-the-output stream command in order to produce output:
While PowerShell does have a counterpart to echo
, namely Write-Output
, its use is rarely needed.
- Among the rare cases where
Write-Output
is useful is preventing enumeration of a collection on output with -NoEnumerate
, or to use common parameter -OutVariable
to both output data and capture it in a variable (however, note that all cmdlets as well as advanced functions / scripts themselves -OutVariable
, so the use of Write-Output
may not be needed). See this answer for more cases.
The implicit output behavior:
Example:
# Define a function that takes an array of integers and
# outputs their hex representation (e.g., '0xa' for decimal 10)
function Get-HexNumber {
param([int[]] $numbers)
foreach ($i in $numbers) {
# Format the integer at hand
# *and implicitly output it*.
'0x{0}' -f $i.ToString('x')
}
}
# Call the function with integers 0 to 16 and loop over the
# results, sleeping 1 second between numbers.
Get-HexNumber (0..16) | ForEach-Object { "[$_]"; Start-Sleep 1 }
The above yields the following:
[0x0]
# 1-second pause
[0x1]
# 1-second pause
[0x2]
...
[0x10]
This demonstrates the streaming aspect of the behavior: Get-HexNumber
's output is available to the ForEach-Object
cmdlet call as it is being produced, not after Get-HexNumber
has exited.
[1] In PowerShell (Core) 6+, Out-Null
has an optimization if the only preceding pipeline segment is a side effect-free expression rather than a method or command call; e.g., 1..1e6 | Out-Null
executes in almost no time, because the expression is seemingly not even executed. However, such a scenario is atypical, and the functionally equivalent Write-Output (1..1e6) | Out-Null
takes a long time to run, much longer than $null = Write-Output (1..1e6)
.
True
typically is a "this worked" success msg. one normally suppresses that with an assignment$Null =
, casting to void[void]$range.BorderAround(1, -4138)
, or by using| Out-Null
. the 2nd is fastest, the 1st is the most common, the 3rd is much slower than the other two. the speed is not important unless you do it frequently, tho. [grin] – Workable