在 PowerShell 中忽略输出的更好(更清洁)方法是啥? [关闭]

Posted

技术标签:

【中文标题】在 PowerShell 中忽略输出的更好(更清洁)方法是啥? [关闭]【英文标题】:What's the better (cleaner) way to ignore output in PowerShell? [closed]在 PowerShell 中忽略输出的更好(更清洁)方法是什么? [关闭] 【发布时间】:2011-07-12 17:17:25 【问题描述】:

假设您有一个返回某些内容的方法或 cmdlet,但您不想使用它并且不想输出它。我找到了这两种方法:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

你用什么?哪种方法更好/更清洁?为什么?

【问题讨论】:

【参考方案1】:

我刚刚对我知道的四个选项进行了一些测试。

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 的明确理由。

【讨论】:

我通常使用 VOID 作为语言行的一部分,如果它已经是一个很大的长管道,则为空 Out-Null 可能是开销。但是.. 如果将一个对象传送到Out-Null 0.076 毫秒,恕我直言,对于脚本语言来说它仍然非常好:) 我有点搞砸了,我通过使用 Out-Null -InputObject ($(1..1000) | ?$_ 获得了 [void] 和 > $null 的最佳性能-is [int])。 好点,这似乎没有明显的开销。 虽然有基准来比较完成某事的不同方式总是好的,但我们不要忽视这里可以得出的另一个结论:除非你不得不放弃价值成千上万次性能差异可以忽略不计。如果这里或那里的毫秒对您来说真的很重要,那么您可能一开始就不应该使用 PowerShell。如果不对选项在这些方面的排名做出任何判断,牺牲速度来换取其他品质(例如可读性和表达程序员意图)不一定是坏事。【参考方案2】:

我意识到这是一个旧线程,但对于那些将@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

【讨论】:

+1 表示这一点很重要。事实上,没有什么可以强迫任何人将Out-Null 与管道一起使用,因此显示管道开销的最佳方法是调用Out-Null 有和没有它。在我的系统上,对于 10,000 次迭代,Out-Null -InputObject $GetProcess 得到 0.576 秒,而$GetProcess | Out-Null 得到 5.656 秒(慢近 10 倍)。 您好科林,感谢您的回答。我运行了您的样本,但在所有批次中 [void]$null 的表现仍然优于 | Out-Null。我知道这是因为管道和增量随着后面的批次而缩小,但在我的机器上 Out-Null 在任何批次中都没有更快的执行。 当然,我必须同意那些认为性能不是一切,可读性也应该考虑的人的观点。 @Hinek 您的评论表明您错过了科林所说的重点。是的,[void]$null 将比 | Out-Null 表现更好——因为 |。试试Out-Null -InputObject (expression) 进行比较。【参考方案3】:

还有Out-Null cmdlet,您可以在管道中使用它,例如Add-Item | Out-Null

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".

【讨论】:

对于 Jason 的回答中的小基准测试结果你怎么看? 非常有趣,尤其是 Out-Null 和其他方法之间的巨大差异。我想我会切换到[void],即使 Out-Null 解决方案看起来更“强大”。 我仍然不确定,我的首选方式是什么。正如 stej 已经评论的那样,即使是这种巨大的差异似乎也并不显着。 @Hinek [void] 看起来很清晰(虽然不像我说的那样强大),你会在行的开头看到这一行没有输出。所以这是另一个优势,如果你在大循环中执行 Out-Null,性能可能是个问题;)【参考方案4】:

我会考虑使用类似的东西:

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,因为很明显输出不会被使用并被丢弃。

【讨论】:

【参考方案5】:

就我个人而言,我使用... | Out-Null,因为正如其他人所评论的那样,与... &gt; $null[void] ... 相比,这看起来更像是“PowerShellish”方法。 $null = ... 正在利用特定的 automatic variable 并且很容易被忽略,而其他方法通过附加语法可以明显看出您打算丢弃表达式的输出。因为... | Out-Null... &gt; $null 出现在表达式的末尾,所以我认为它们有效地传达了“把我们到目前为止所做的一切都扔掉”,而且您可以更轻松地将它们注释掉以进行调试(例如@ 987654328@),而不是将 $null =[void] 放在表达式之前,以确定执行它之后会发生什么。

不过,让我们看一个不同的基准:不是执行每个选项所需的时间,而是弄清楚每个选项的作用所需的时间。在与完全没有 PowerShell 甚至脚本经验的同事一起工作过的环境中,我倾向于尝试以一种多年后出现的人甚至可能不理解他们正在查看的语言的方式来编写我的脚本争取机会弄清楚它在做什么,因为他们可能不得不支持或更换它。到目前为止,我从来没有想过这是使用一种方法而不是其他方法的理由,但是想象一下你处于那个位置并且你使用help 命令或你最喜欢的搜索引擎来尝试找出Out-Null 的作用.您会立即获得有用的结果,对吗?现在尝试对[void]$null = 做同样的事情。没那么容易吧?

诚然,与理解脚本的整体逻辑相比,抑制值的输出只是一个很小的细节,而且您只能尝试“简化”代码这么多,然后才能牺牲自己的编写能力新手阅读能力的代码......不太好的代码。我的观点是,有可能一些精通 PowerShell 的人甚至不熟悉 [void]$null = 等,并且仅仅因为这些可能执行得更快或键入的击键次数更少,并不意味着他们'重新做你想做的事情的最好方法,仅仅因为一种语言给你古怪的语法并不意味着你应该使用它而不是更清晰和更知名的东西。*

* 我假设Out-Null 是清晰且众所周知的,我不知道它是$true。无论认为哪个选项对您的代码的未来读者和编辑者(包括您自己)来说最清晰和最容易访问,无论输入时间或执行时间如何,这都是我推荐的选项你用。

【讨论】:

以上是关于在 PowerShell 中忽略输出的更好(更清洁)方法是啥? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在Powershell中忽略来自FFMPEG控制台输出的特定警告

powershell PS文件名清洁剂

powershell 清洁解决方案文件

在Clojure中对地图矢量进行排序和排序的更清洁方法?

从 C++ 应用程序获取 PowerShell 脚本输出

将PowerShell的默认输出编码更改为UTF-8