What's the better (cleaner) way to ignore output in PowerShell? [closed]
Asked Answered
E

5

175

Let's say you have a method or a cmdlet that returns something, but you don't want to use it and you don't want to output it. I found these two ways:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

What do you use? Which is the better/cleaner approach? Why?

Excite answered 10/3, 2011 at 13:15 Comment(0)
W
231

I just did some tests of the four options that I know about.

Measure-Command {$(1..1000) | Out-Null}

TotalMilliseconds : 76.211

Measure-Command {[Void]$(1..1000)}

TotalMilliseconds : 0.217

Measure-Command {$(1..1000) > $null}

TotalMilliseconds : 0.2478

Measure-Command {$null = $(1..1000)}

TotalMilliseconds : 0.2122

## Control, times vary from 0.21 to 0.24
Measure-Command {$(1..1000)}

TotalMilliseconds : 0.2141

So I would suggest that you use anything but Out-Null due to overhead. The next important thing, to me, would be readability. I kind of like redirecting to $null and setting equal to $null myself. I use to prefer casting to [Void], but that may not be as understandable when glancing at code or for new users.

I guess I slightly prefer redirecting output to $null.

Do-Something > $null

Edit

After stej's comment again, I decided to do some more tests with pipelines to better isolate the overhead of trashing the output.

Here are some tests with a simple 1000 object pipeline.

## Control Pipeline
Measure-Command {$(1..1000) | ?{$_ -is [int]}}

TotalMilliseconds : 119.3823

## Out-Null
Measure-Command {$(1..1000) | ?{$_ -is [int]} | Out-Null}

TotalMilliseconds : 190.2193

## Redirect to $null
Measure-Command {$(1..1000) | ?{$_ -is [int]} > $null}

TotalMilliseconds : 119.7923

In this case, Out-Null has about a 60% overhead and > $null has about a 0.3% overhead.

Addendum 2017-10-16: I originally overlooked another option with Out-Null, the use of the -inputObject parameter. Using this the overhead seems to disappear, however the syntax is different:

Out-Null -inputObject ($(1..1000) | ?{$_ -is [int]})

And now for some tests with a simple 100 object pipeline.

## Control Pipeline
Measure-Command {$(1..100) | ?{$_ -is [int]}}

TotalMilliseconds : 12.3566

## Out-Null
Measure-Command {$(1..100) | ?{$_ -is [int]} | Out-Null}

TotalMilliseconds : 19.7357

## Redirect to $null
Measure-Command {$(1..1000) | ?{$_ -is [int]} > $null}

TotalMilliseconds : 12.8527

Here again Out-Null has about a 60% overhead. While > $null has an overhead of about 4%. The numbers here varied a bit from test to test (I ran each about 5 times and picked the middle ground). But I think it shows a clear reason to not use Out-Null.

Wnw answered 10/3, 2011 at 18:1 Comment(10)
i typically use VOID for things that are part of a language line, and out-null if its already a big long pipeline anyhowCuesta
Out-Null is maybe overhead. But.. if piping one object to Out-Null 0.076 milliseconds, imho it's still perfectly fine for scripting language :)Felecia
I was messing with this a little, and I get the best performance over [void] and > $null by using Out-Null -InputObject ($(1..1000) | ?{$_ -is [int]}).Hesiod
Good point, that seems to have no noticeable overhead.Wnw
While it's always good to have benchmarks to compare different ways of accomplishing something, let's not overlook the other conclusion that can be drawn here: unless you're having to discard values hundreds of thousands of times the performance differences are negligible. And if a millisecond here or there really matters to you, you probably shouldn't be using PowerShell in the first place. Without making any judgment about how the options rank in these regards, it's not necessarily a bad thing to sacrifice speed for other qualities such as readability and expressing programmer intent.Darksome
Also, better (from a performance perspective) than Out-Null is "Out-Void"... i.e. function Out-Void{}Cannice
Someone could also override the behaviour of Out-Null... Which has pros and cons; though mostly the latter. function Out-Null {Param([Parameter(ValueFromPipeline)]$InputObject)Process{$InputObject}}; 1..10 | Out-NullCannice
Actually with the advent of PowerShell Core, Out-Null is only a bit slower than > $null, about 2x slower and I think it's acceptable.Praenomen
If anyone wants to easily test this out on their own computer, here's a function to do so: function TestSpeed($count) { Measure-Command {$(1..$count) | Out-Null} Measure-Command {[Void]$(1..$count)} Measure-Command {$(1..$count) > $null} Measure-Command {$null = $(1..$count)} } Then you can check the output with TestSpeed 50000 | Select-Object -ExpandProperty TotalMilliseconds or whatever number you want for your machine. You will also get different performance by wrapping the first command in parentheses.Batts
An important readability concern is where to look. $null = looks a bit strange at first, but it's declared right at the start of the line where you can easily see it. It's also good to know that it isn't costly, so thanks.Undershoot
W
46

I realize this is an old thread, but for those taking @JasonMArcher's accepted answer above as fact, I'm surprised it has not been corrected many of us have known for years it is actually the PIPELINE adding the delay and NOTHING to do with whether it is Out-Null or not. In fact, if you run the tests below you will quickly see that the same "faster" casting to [void] and $void= that for years we all used thinking it was faster, are actually JUST AS SLOW and in fact VERY SLOW when you add ANY pipelining whatsoever. In other words, as soon as you pipe to anything, the whole rule of not using out-null goes into the trash.

Proof, the last 3 tests in the list below. The horrible Out-null was 32339.3792 milliseconds, but wait - how much faster was casting to [void]? 34121.9251 ms?!? WTF? These are REAL #s on my system, casting to VOID was actually SLOWER. How about =$null? 34217.685ms.....still friggin SLOWER! So, as the last three simple tests show, the Out-Null is actually FASTER in many cases when the pipeline is already in use.

So, why is this? Simple. It is and always was 100% a hallucination that piping to Out-Null was slower. It is however that PIPING TO ANYTHING is slower, and didn't we kind of already know that through basic logic? We just may not have know HOW MUCH slower, but these tests sure tell a story about the cost of using the pipeline if you can avoid it. And, we were not really 100% wrong because there is a very SMALL number of true scenarios where out-null is evil. When? When adding Out-Null is adding the ONLY pipeline activity. In other words....the reason a simple command like $(1..1000) | Out-Null as shown above showed true.

If you simply add an additional pipe to Out-String to every test above, the #s change radically (or just paste the ones below) and as you can see for yourself, the Out-Null actually becomes FASTER in many cases:

$GetProcess = Get-Process

# Batch 1 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Out-Null 
} 
}).TotalMilliseconds

# Batch 1 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess) 
} 
}).TotalMilliseconds

# Batch 1 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess 
} 
}).TotalMilliseconds

# Batch 2 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Select-Object -Property ProcessName | Out-Null 
} 
}).TotalMilliseconds

# Batch 2 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess | Select-Object -Property ProcessName ) 
} 
}).TotalMilliseconds

# Batch 2 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess | Select-Object -Property ProcessName 
} 
}).TotalMilliseconds

# Batch 3 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Select-Object -Property Handles, NPM, PM, WS, VM, CPU, Id, SI, Name | Out-Null 
} 
}).TotalMilliseconds

# Batch 3 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess | Select-Object -Property Handles, NPM, PM, WS, VM, CPU, Id, SI, Name ) 
} 
}).TotalMilliseconds

# Batch 3 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess | Select-Object -Property Handles, NPM, PM, WS, VM, CPU, Id, SI, Name 
} 
}).TotalMilliseconds

# Batch 4 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Out-String | Out-Null 
} 
}).TotalMilliseconds

# Batch 4 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess | Out-String ) 
} 
}).TotalMilliseconds

# Batch 4 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess | Out-String 
} 
}).TotalMilliseconds
Wilbert answered 8/8, 2017 at 20:19 Comment(5)
+1 for making this important point. In fact, there's nothing forcing anyone to use Out-Null with a pipeline at all, so the best way to show the overhead of a pipeline is to invoke Out-Null with and without it. On my system, for 10,000 iterations I get 0.576 seconds for Out-Null -InputObject $GetProcess vs. 5.656 seconds (nearly 10x slower) for $GetProcess | Out-Null.Darksome
Hi Collin, thanks for your answer. I ran your samples, but in all batches [void] and $null still performed a better than | Out-Null. I get that this is because of the pipeline and the delta shrinks with the later batches, but on my machine Out-Null doesn't perform faster in any of the batches.Excite
But of course I have to agree with those, who argue, that performance isn't everything and readability should factor, too.Excite
@Excite Your comment suggests you missed the point of what Collin was saying. Yes, [void] and $null will perform better than | Out-Null -- because of the |. Try Out-Null -InputObject (expression) for comparison.Weyermann
@Collin Chaffin Why didn't you include the output of your script in the answer ? Of course people can run it on their own system, but it would be much better to add the results directly in the answer for knowledge sharing sake, posterity and also to prevent countless people using superfluous CPU cycles when most would have been sufficiently convinced by your resultsWeave
M
21

There is also the Out-Null cmdlet, which you can use in a pipeline, for example, Add-Item | Out-Null.

Manual page for Out-Null

NAME
    Out-Null

SYNOPSIS
    Deletes output instead of sending it to the console.


SYNTAX
    Out-Null [-inputObject <psobject>] [<CommonParameters>]


DETAILED DESCRIPTION
    The Out-Null cmdlet sends output to NULL, in effect, deleting it.


RELATED LINKS
    Out-Printer
    Out-Host
    Out-File
    Out-String
    Out-Default

REMARKS
     For more information, type: "get-help Out-Null -detailed".
     For technical information, type: "get-help Out-Null -full".
Marchand answered 10/3, 2011 at 13:37 Comment(4)
What do you think about the results of the little benchmark test in Jason's answer?Excite
Very interesting, especially the really huge difference between Out-Null and the other methods. I think I will switch to [void] even though the Out-Null solution looks more "powershellish".Marchand
I'm still not sure, what's my preferred way. Even this huge difference seems hardly significant as stej already commented.Excite
@Excite [void] looks very clear (tho not powershellish as I said), you'll see at the beginning of the line that there is no output in this line. So this is another advantage, and if you do a Out-Null in big loop, performance could be an issue ;)Marchand
F
13

I would consider using something like:

function GetList
{
  . {
     $a = new-object Collections.ArrayList
     $a.Add(5)
     $a.Add('next 5')
  } | Out-Null
  $a
}
$x = GetList

Output from $a.Add is not returned -- that holds for all $a.Add method calls. Otherwise you would need to prepend [void] before each the call.

In simple cases I would go with [void]$a.Add because it is quite clear that output will not be used and is discarded.

Felecia answered 10/3, 2011 at 13:52 Comment(0)
D
10

Personally, I use ... | Out-Null because, as others have commented, that looks like the more "PowerShellish" approach compared to ... > $null and [void] .... $null = ... is exploiting a specific automatic variable and can be easy to overlook, whereas the other methods make it obvious with additional syntax that you intend to discard the output of an expression. Because ... | Out-Null and ... > $null come at the end of the expression I think they effectively communicate "take everything we've done up to this point and throw it away", plus you can comment them out easier for debugging purposes (e.g. ... # | Out-Null), compared to putting $null = or [void] before the expression to determine what happens after executing it.

Let's look at a different benchmark, though: not the amount of time it takes to execute each option, but the amount of time it takes to figure out what each option does. Having worked in environments with colleagues who were not experienced with PowerShell or even scripting at all, I tend to try to write my scripts in a way that someone coming along years later that might not even understand the language they're looking at can have a fighting chance at figuring out what it's doing since they might be in a position of having to support or replace it. This has never occurred to me as a reason to use one method over the others until now, but imagine you're in that position and you use the help command or your favorite search engine to try to find out what Out-Null does. You get a useful result immediately, right? Now try to do the same with [void] and $null =. Not so easy, is it?

Granted, suppressing the output of a value is a pretty minor detail compared to understanding the overall logic of a script, and you can only try to "dumb down" your code so much before you're trading your ability to write good code for a novice's ability to read...not-so-good code. My point is, it's possible that some who are fluent in PowerShell aren't even familiar with [void], $null =, etc., and just because those may execute faster or take less keystrokes to type, doesn't mean they're the best way to do what you're trying to do, and just because a language gives you quirky syntax doesn't mean you should use it instead of something clearer and better-known.*

* I am presuming that Out-Null is clear and well-known, which I don't know to be $true. Whichever option you feel is clearest and most accessible to future readers and editors of your code (yourself included), regardless of time-to-type or time-to-execute, that's the option I'm recommending you use.

Darksome answered 5/8, 2018 at 21:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.