在 PowerShell 中忽略输出的更好(更干净)方法是什么?

假设您有一个方法或 cmdlet,它返回一些内容,但是您不想使用它,也不想输出它。我发现了两种方法:

Add-Item > $null


[void]Add-Item


Add-Item | Out-Null

你用什么方法? 哪种方法更好/更干净? 为什么?

142299 次浏览

还有 Out-Nullcmdlet,您可以在管道中使用它,例如 Add-Item | 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".

我会考虑使用这样的东西:

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

不返回来自 $a.Add的输出——这对所有 $a.Add方法调用都适用。否则,您将需要在每次调用之前预先设置 [void]

在简单的情况下,我会使用 [void]$a.Add,因为很明显,输出将不会被使用,并被丢弃。

我只是对我知道的四个选项做了一些测试。

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

因此,我建议您使用任何东西,但由于开销 Out-Null。对我来说,下一个重要的事情是可读性。我有点喜欢重定向到 $null和设置等于 $null自己。比起 [Void],我更喜欢强制转换,但是在浏览代码或者对于新用户来说,这可能不那么容易理解。

我想我更喜欢将输出重定向到 $null

Do-Something > $null

剪辑

在再次听取了 stej 的注释之后,我决定使用管道进行更多的测试,以更好地隔离清除输出的开销。

下面是一些使用简单的1000对象管道的测试。

## 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

在这种情况下,Out-Null大约有60% 的开销,而 > $null大约有0.3% 的开销。

附录2017-10-16: 我最初忽略了 Out-Null的另一个选项,即使用 -inputObject参数。使用这种方法,开销似乎消失了,但语法是不同的:

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

现在用一个简单的100对象管道进行一些测试。

## 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

这里同样 Out-Null有大约60% 的开销。而 > $null的开销约为4% 。这里的数字在不同的测试中有所不同(每个测试我都运行了大约5次,并选择了中间地带)。但是我认为这清楚地说明了不使用 Out-Null的理由。

我意识到这是一个老线程,但是对于那些把@JasonMarcher 上面接受的答案当作事实的人来说,我很惊讶它没有被更正,我们很多人已经知道多年了,它实际上是管道增加了延迟,与它是否为 Out-Null 没有任何关系。事实上,如果你运行下面的测试,你会很快发现,相同的“更快”铸造[ void ]和 $void = 多年来我们都认为它是更快的,实际上只是一样慢,事实上非常慢,当你添加任何管道什么。换句话说,一旦您管道到任何东西,不使用 out-null 的整个规则就会被丢进垃圾桶。

证明,下面列表中的最后3个测试。可怕的 Out-null 是32339.3792毫秒,但是等等——释放到[ void ]的速度有多快?34121.9251毫秒? ! ?卧槽?在我的系统中,这些都是真实的,释放到 VOID 的速度实际上更慢。= $null 怎么样?34217.685毫秒... 还是他妈的慢点!因此,正如前三个简单测试所显示的,在管道已经在使用的许多情况下,Out-Null 实际上更快。

为什么会这样?很简单。这是,而且一直是100% 的幻觉,管道到 Out-Null 是较慢的。然而,管道到任何东西是较慢的,我们不是已经知道,通过基本的逻辑?我们可能不知道会慢多少,但是这些测试肯定会告诉您如果可以避免的话,使用管道的成本。而且,我们并不是真的100% 错误,因为只有极少数的真实场景中,out-null 是邪恶的。什么时候?当添加 Out-Null 时正在添加 ONLY 管道活动。换句话说... ... 像 $(1。. 1000) | Out-Null 如上所示为 true。

如果你只是简单地在 out-String 中添加一个管道到上面的每个测试中,# s 就会发生根本性的变化(或者只是粘贴下面的那些) ,正如你自己所看到的,Out-Null 实际上在很多情况下变得更快:

$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

就我个人而言,我使用 ... | Out-Null是因为,正如其他人所评论的那样,与 ... > $null[void] ...相比,它看起来更像是“ PowerShellish”方法。$null = ...正在利用一个特定的 自动变量,这个 自动变量很容易被忽略,而其他方法通过附加的语法使其显而易见,您打算放弃表达式的输出。因为 ... | Out-Null... > $null出现在表达式的末尾,我认为它们有效地表达了“把我们到目前为止所做的一切都扔掉”,而且为了调试的目的,你可以更容易地将它们注释掉(例如 ... # | Out-Null) ,相比之下,把 $null =[void] ... > $null0作为表达式来决定 ... > $null1执行它时发生了什么。

不过,让我们看看不同的基准测试: 不是执行每个选项所需的时间,而是执行 弄清楚每个选项的作用所需的时间。与没有使用 PowerShell 甚至脚本经验的同事一起工作过,我倾向于尝试用这样一种方式来写我的脚本: 几年后,有些人可能甚至不理解他们正在使用的语言,因为他们可能不得不支持或替换它,所以他们可能有机会弄清楚它在做什么。到现在为止,我从来没有想过要用一种方法来代替其他的方法,但是想象一下你处在这样的位置,你使用 help命令或者你最喜欢的搜索引擎来尝试找出 Out-Null是做什么的。你马上就能得到有用的结果,对吧?现在尝试对 [void]$null =做同样的操作。没那么容易,对吧?

当然,与理解一个脚本的整体逻辑相比,抑制一个值的输出只是一个非常小的细节,而且在你把自己写好代码的能力换成一个初学者读... ... 不太好的代码的能力之前,你只能尝试“简化”你的代码。我的观点是,可能有些熟悉 PowerShell 的人甚至不熟悉 [void]$null =等等,仅仅因为它们可能执行得更快或者键入的次数更少,并不意味着它们就是你正在尝试做的事情的最佳方式,仅仅因为一种语言给了你古怪的语法,并不意味着你应该使用它而不是一些更清晰和更广为人知的东西。*

我假设 Out-Null是清楚的和众所周知的,而我不知道它是 $true。无论哪个选项 感觉是最清楚和最容易为您的代码的未来读者和编辑(包括您自己) ,无论时间到类型或时间到执行,这是我推荐您使用的选项。