Powershell Write-Output is a default output behaviour. The question is why do we need it at all?
Asked Answered
W

2

2

As described here https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-output all Write-Output does is sends the output to the current output device, which is a default anyway.

It appears, that the only advantage of having explicitly used Write-Output is the ability to provide a parameter -NoEnumerate. Is that correct?

If not, then what are other cases for using Write-Output?

Wabash answered 22/5, 2023 at 12:3 Comment(3)
Can be also useful to flatten a collection. And thats about itLassa
I believe it mostly exists for consistency (there is one cmdlet for each of the standard streams) and to provide some inter-language compatibility (echo is an alias for Write-Output).Wilcox
thank you @zett42, yes, "consistency" seems to be the right descriptionWabash
E
1

I think the main advantage really comes down to readability of your code, as it becomes easy at a glance to see that you're sending something to the output.

For instance, even with something as simple as

$foo = "Current date is $(get-date)"

I would argue that scanning through lines of code it's far easier to instantly understand what's happening when you see

Write-Output $foo

than if you just see

$foo

on a line of its own.

It's perhaps worth noting that shortened forms appear to be going out of popularity, or no longer being recommended in the same way. For instance if you edit Powershell code with VS Code you'll perhaps notice it showing warnings for the use of aliases, for instance ft aliasing Format-Table, select aliasing Select-Object etc, with the same listed reasoning that it makes it harder to maintain the code.

Emmettemmey answered 22/5, 2023 at 13:26 Comment(1)
When starting with PowerShell, I had similar thoughts, using Write-Output for readability. Now I think that in many situations where you rely on PowerShell's implicit output behavior, Write-Output would actually make the code less readable, due to its verbosity. Also there is a performance penalty due to the additional function call. I like my code style to be consistent, so I've refrained from using Write-Output, but I add a comment when a variable stands on its own line, e. g. $foo # output to make my intentions clear.Wilcox
S
2

Let me complement Keith Langmead's answer with a - hopefully exhaustive - list of reasons to use Write-Output:

  • For a discussion of PowerShell's implicit output behavior, see this answer.

(Debatable) readability: Write-Output as an explicit signal of the intent to produce output:

  • Keith's answer suggests that use of Write-Output helps readability and maintainability in that it signals the intent to produce output explicitly.

  • zett42 notes that this can make code more verbose and also incurs a performance penalty, causing him to prefer implicit output, but decorating it with a comment to signal the intent; e.g. $foo # output

  • I agree: Making explicit what the language implicitly provides is redundant, and may give rise to the misconception in inexperienced readers of the code that Write-Output is required (analogous to how you actually need echo in cmd.exe and Bash, for instance; as noted echo is a built-in alias of Write-Output in PowerShell); to the experienced reader, such calls amount to noise.

  • In a manner of speaking, explicit Write-Output use expresses an ongoing disagreement with one the language's core features, which - while unfamiliar to users of other languages - can be embraced, resulting in more concise, distraction-free code that also performs better.

Outputting arrays (list-like collections) as a whole, i.e. as a single object, with Write-Output -NoEnumerate:

  • As you note, the -NoEnumerate switch can be used to achieve that; e.g.:

     # -> 'Array has 3 elements', i.e. $_ refers to the *entire array*
     #    created with (1..3), due to -NoEnumerate.
     Write-Output -NoEnumerate (1..3) | 
       ForEach-Object { "Array has $($_.Count) elements." }
    
  • However, there is a more concise - albeit somewhat obscure to beginners - and better-performing alternative: use of the unary form of , the array constructor operator to wrap an array in a transient, single-element array, which, when enumerated in the pipeline as usual, results in the wrapped array being sent as a whole:

     # -> 'Array has 3 elements', i.e. $_ refers to the *entire array*
     #    created with (1..3), due to wrapping (1..3) in a 
     #    transient, single-element wrapper array.
     , (1..3) | 
       ForEach-Object { "Array has $($_.Count) elements." }
    

Flattening arrays (... | Write-Output):

  • As Santiago Squarzon points out, piping multiple arrays to Write-Output implicitly flattens them, i.e. returns a single-dimensional array that contains all the elements of the input arrays; e.g.:

    # Create a jagged array (an array of arrays)
    # -> @( @(1, 2, 3), @(4, 5) )
    $arrayOfArrays = (1..3), (4..5)
    
    # Flatten it by piping to Write-Output:
    # -> @(1, 2, 3, 4, 5)
    $arrayOfArrays | Write-Output 
    

Use with common parameters, notably -OutVariable and -PipelineVariable:

  • -OutVariable (-ov) allows you to save Write-Output's output to a variable of choice while also sending the output through the pipeline.

  • -PipelineVariable (-pv) saves each output object to a variable of choice that can then be accessed in the script block arguments of subsequent pipeline segments.

  • Note: Given that these are common parameters, you won't need Write-Output if your output is produced by cmdlets or the cmdlet-like advanced functions and scripts, because you can use these parameters directly with such commands. However, you do need Write-Host in order to use these common parameters if the output is produced by simple functions and scripts or external programs; e.g. (these are contrived, but simple examples):

     # Stores the output from cmd /c dir /b /A:d in variable $dirOutput
     # but also sends it through the pipeline to Measure-Object
     Write-Output -OutVariable dirOutput (cmd /c dir /b /A:d) | Measure-Object
    
     # Stores each directory name output by cmd /c dir /b /A:d in 
     # variable $thisDir and previews (-WhatIf) a Rename-Item operation
     # that prepends "new_" to each directory name.
     Write-Output -PipelineVariable thisDir (cmd /c dir /b /A:d) |
       Rename-Item -NewName { "new_" + $thisDir } -WhatIf
    

Syntactic convenience: creation of arrays of (simple) strings:

  • Given that strings as command arguments only need quoting if they contain special characters, notably spaces, use of Write-Output offers a syntactically simpler alternative to array literals.

  • Additionally, you can then omit , between the elements, given that Write-Output allows specifying arguments individually.

  • This technique is probably best limited to interactive use and "throw-away" scripts.

  • For instance; note that elements with metacharacters (e.g. two and a half) would need quoting either way ('two and a half'):

    # Array literal (@(...) enclosure is optional)
    @('one', 'two', 'three')
    
    # Syntactically simpler Write-Output equivalent, using its 'echo' alias:
    echo one two three
    
Salcido answered 23/5, 2023 at 17:30 Comment(0)
E
1

I think the main advantage really comes down to readability of your code, as it becomes easy at a glance to see that you're sending something to the output.

For instance, even with something as simple as

$foo = "Current date is $(get-date)"

I would argue that scanning through lines of code it's far easier to instantly understand what's happening when you see

Write-Output $foo

than if you just see

$foo

on a line of its own.

It's perhaps worth noting that shortened forms appear to be going out of popularity, or no longer being recommended in the same way. For instance if you edit Powershell code with VS Code you'll perhaps notice it showing warnings for the use of aliases, for instance ft aliasing Format-Table, select aliasing Select-Object etc, with the same listed reasoning that it makes it harder to maintain the code.

Emmettemmey answered 22/5, 2023 at 13:26 Comment(1)
When starting with PowerShell, I had similar thoughts, using Write-Output for readability. Now I think that in many situations where you rely on PowerShell's implicit output behavior, Write-Output would actually make the code less readable, due to its verbosity. Also there is a performance penalty due to the additional function call. I like my code style to be consistent, so I've refrained from using Write-Output, but I add a comment when a variable stands on its own line, e. g. $foo # output to make my intentions clear.Wilcox

© 2022 - 2024 — McMap. All rights reserved.