【发布时间】:2011-07-12 17:17:25
【问题描述】:
假设您有一个返回某些内容的方法或 cmdlet,但您不想使用它并且不想输出它。我找到了这两种方法:
Add-Item > $null
[void]Add-Item
Add-Item | Out-Null
你用什么?哪种方法更好/更清洁?为什么?
【问题讨论】:
标签: powershell null void
假设您有一个返回某些内容的方法或 cmdlet,但您不想使用它并且不想输出它。我找到了这两种方法:
Add-Item > $null
[void]Add-Item
Add-Item | Out-Null
你用什么?哪种方法更好/更清洁?为什么?
【问题讨论】:
标签: powershell null void
还有Out-Null cmdlet,您可以在管道中使用它,例如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".
【讨论】:
[void],即使 Out-Null 解决方案看起来更“强大”。
[void] 看起来很清晰(虽然不像我说的那样强大),你会在行的开头看到这一行没有输出。所以这是另一个优势,如果你在大循环中执行 Out-Null,性能可能是个问题;)
我会考虑使用类似的东西:
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 的明确理由。
【讨论】:
Out-Null 可能是开销。但是.. 如果将一个对象传送到Out-Null 0.076 毫秒,恕我直言,对于脚本语言来说它仍然非常好:)
我意识到这是一个旧线程,但对于那些将@JasonMArcher 接受的上述答案作为事实的人,我很惊讶它没有得到纠正,我们中的许多人多年来都知道它实际上是增加延迟的管道,而没有与它是否为Out-Null有关。事实上,如果你运行下面的测试,你会很快看到相同的“更快”转换为 [void] 和 $void= 多年来我们都认为它更快,实际上只是一样慢,实际上非常慢您添加任何流水线。换句话说,只要你管到任何东西,不使用 out-null 的整个规则都会进入垃圾箱。
证明,下面列表中的最后 3 个测试。可怕的 Out-null 是 32339.3792 毫秒,但是等等 - 投射到 [void] 的速度有多快? 34121.9251 毫秒?!?怎么回事?这些是我系统上的真实#s,投射到 VOID 实际上更慢。 =$null 怎么样? 34217.685ms .....还是慢点!因此,正如最后三个简单测试所示,当管道已在使用中时,Out-Null 实际上在许多情况下更快。
那么,这是为什么呢?简单的。到 Out-Null 的管道速度较慢一直是 100% 的幻觉。然而,管道到任何东西的速度都比较慢,而且我们不是已经通过基本逻辑知道了这一点吗?我们可能只是不知道慢了多少,但是如果可以避免的话,这些测试肯定会讲述使用管道的成本。而且,我们并不是真的 100% 错了,因为在极少数真实场景中,out-null 是邪恶的。什么时候?添加 Out-Null 时添加唯一的管道活动。换句话说....像 $(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 与管道一起使用,因此显示管道开销的最佳方法是调用Out-Null 有和没有它。在我的系统上,对于 10,000 次迭代,Out-Null -InputObject $GetProcess 得到 0.576 秒,而$GetProcess | Out-Null 得到 5.656 秒(慢近 10 倍)。
[void] 和 $null 的表现仍然优于 | Out-Null。我知道这是因为管道和增量随着后面的批次而缩小,但在我的机器上 Out-Null 在任何批次中都没有更快的执行。
[void] 和 $null 将比 | Out-Null 表现更好——因为 |。试试Out-Null -InputObject (expression) 进行比较。
就我个人而言,我使用... | Out-Null,因为正如其他人所评论的那样,与... > $null 和[void] ... 相比,这看起来更像是“PowerShellish”方法。 $null = ... 正在利用特定的 automatic variable 并且很容易被忽略,而其他方法通过附加语法可以明显看出您打算丢弃表达式的输出。因为... | Out-Null 和... > $null 出现在表达式的末尾,所以我认为它们有效地传达了“把我们到目前为止所做的一切都扔掉”,而且您可以更轻松地将它们注释掉以进行调试(例如@ 987654328@),而不是将 $null = 或 [void] 放在表达式之前,以确定执行它之后会发生什么。
不过,让我们看一个不同的基准:不是执行每个选项所需的时间,而是弄清楚每个选项的作用所需的时间。在与完全没有 PowerShell 甚至脚本经验的同事一起工作过的环境中,我倾向于尝试以一种多年后出现的人甚至可能不理解他们正在查看的语言的方式来编写我的脚本争取机会弄清楚它在做什么,因为他们可能不得不支持或更换它。到目前为止,我从来没有想过这是使用一种方法而不是其他方法的理由,但是想象一下你处于那个位置并且你使用help 命令或你最喜欢的搜索引擎来尝试找出Out-Null 的作用.您会立即获得有用的结果,对吗?现在尝试对[void] 和$null = 做同样的事情。没那么容易吧?
诚然,与理解脚本的整体逻辑相比,抑制值的输出只是一个很小的细节,而且您只能尝试“简化”代码这么多,然后才能牺牲自己的编写能力新手阅读能力的代码......不太好的代码。我的观点是,有可能一些精通 PowerShell 的人甚至不熟悉 [void]、$null = 等,并且仅仅因为这些可能执行得更快或键入的击键次数更少,并不意味着他们'重新做你想做的事情的最好方法,仅仅因为一种语言给你古怪的语法并不意味着你应该使用它而不是更清晰和更知名的东西。*
* 我假设Out-Null 是清晰且众所周知的,我不知道它是$true。无论您认为哪个选项对您的代码的未来读者和编辑者(包括您自己)来说最清晰和最容易访问,无论输入时间或执行时间如何,这都是我推荐的选项你用。
【讨论】: