Why does Range.BorderAround emit “True” to the con

2020-02-13 04:51发布

Using BorderAround emits "True" to the console.

$range = $sum_wksht.Range('B{0}:G{0}' -f ($crow))
$range.BorderAround(1, -4138)

This can be overcome by using one of the following.

$wasted = $range.BorderAround(1, -4138)
[void]$range.BorderAround(1, -4138)

Why is this needed? Am I not creating the range correctly? Is there a better workaround?

1条回答
2楼-- · 2020-02-13 05:24

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
    • [void] = (...) does that too, but often requires you to enclose the rest of the statement in (...) for syntactic reasons; e.g., [void] 1 + 2 doesn't work as intended, only [void] (1 + 2)
  • 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 with expression output in PowerShell Core).

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 (which is generally only needed for expressions, because cmdlets and advanced functions / scripts themselves support -OutVariable).
    • The implicit output behavior:

      • is generally a blessing:

        • for interactive experimentation - just type any statement - notably including expressions such as [IO.Path]::GetExtension('foo.txt') and [math]::Pow(2, 32) - and see its output (akin to the behavior of a REPL).
        • for writing concise code that doesn't need to spell out implied behavior (see example below).
      • can occasionally be a pitfall:

        • for users accustomed to the semantics of traditional programming languages.

        • due to the potential for accidental pollution of the output stream from statements that one doesn't expect to produce output, such as in your case; a more typical example is the .Add() method of the [System.Collections.ArrayList] class unexpectedly producing output.

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.

查看更多
登录 后发表回答