Qual é a melhor maneira (mais limpa) de ignorar a saída no PowerShell? [fechadas]

134

Digamos que você tenha um método ou cmdlet que retorne algo, mas você não deseja usá-lo e não deseja produzi-lo. Eu encontrei estas duas maneiras:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

O que você usa? Qual é a abordagem melhor / mais limpa? Por quê?

Hinek
fonte
1
como a versão [vazia] ... ainda não foi para mim, mas vou tentar me lembrar dela.
Massif

Respostas:

181

Acabei de fazer alguns testes das quatro opções que conheço.

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

Então, eu sugiro que você use qualquer coisa, menos Out-Nulldevido a sobrecarga. A próxima coisa importante, para mim, seria a legibilidade. Eu meio que gosto de redirecionar $nulle definir igual a $nullmim mesmo. Eu prefiro [Void]transmitir a transmissão , mas isso pode não ser tão compreensível quando se olha para o código ou para novos usuários.

Acho que prefiro redirecionar a saída para $null.

Do-Something > $null

Editar

Após o comentário de stej novamente, decidi fazer mais alguns testes com pipelines para isolar melhor a sobrecarga de descartar a saída.

Aqui estão alguns testes com um pipeline simples de 1000 objetos.

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

Nesse caso, Out-Nullpossui uma sobrecarga de cerca de 60% e uma sobrecarga de > $nullcerca de 0,3%.

Adendo 16-10-2017: Eu originalmente ignorei outra opção com Out-Nullo uso do -inputObjectparâmetro. Usando isso, a sobrecarga parece desaparecer, no entanto, a sintaxe é diferente:

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

E agora, para alguns testes com um pipeline simples de 100 objetos.

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

Aqui, novamente, Out-Nullhá cerca de 60% de sobrecarga. Enquanto > $nulltem uma sobrecarga de cerca de 4%. Os números aqui variaram um pouco de teste para teste (eu corri cada cinco vezes e escolhi o meio termo). Mas acho que mostra um motivo claro para não usar Out-Null.

JasonMArcher
fonte
1
Eu normalmente uso VOID para as coisas que fazem parte de uma linha de idioma e out-null se a sua já um grande oleoduto longo de qualquer forma
klumsy
25
Out-Nullé talvez sobrecarga. Mas .. se tubulação um objeto para Out-Null0,076 milissegundos, IMHO ainda é perfeitamente bem para linguagem de script :)
stej
1
Eu estava mexendo um pouco com isso, e obtive o melhor desempenho sobre [void] e> $ null usando Out-Null -InputObject ($ (1..1000) |? {$ _ -Is [int]}).
tomohulk
1
Bom ponto, isso parece não ter uma sobrecarga perceptível.
JasonMArcher
5
Embora seja sempre bom ter benchmarks para comparar diferentes maneiras de realizar algo, não vamos esquecer a outra conclusão que pode ser tirada aqui: a menos que você precise descartar valores centenas de milhares de vezes, as diferenças de desempenho são desprezíveis. E se um milissegundo aqui ou ali realmente importa para você, você provavelmente não deveria estar usando o PowerShell em primeiro lugar. Sem fazer nenhum julgamento sobre como as opções se classificam nesses aspectos, não é necessariamente uma coisa ruim sacrificar a velocidade por outras qualidades, como legibilidade e expressão da intenção do programador.
BACON
22

Sei que esse é um tópico antigo, mas, para aqueles que consideram a resposta aceita pelo @ JasonMArcher acima como um fato, estou surpreso que isso não tenha sido corrigido. Muitos de nós sabemos há anos que é o PIPELINE que adiciona o atraso e nada a ver com a questão de saber se é Out-Null ou não. De fato, se você executar os testes abaixo, verá rapidamente que o mesmo elenco "mais rápido" para [void] e $ void = que, durante anos, todos nós pensamos que era mais rápido, na verdade são MUITO LENTO e de fato MUITO LENTO quando você adiciona QUALQUER canalização. Em outras palavras, assim que você direciona para qualquer coisa, toda a regra de não usar nulo fora entra no lixo.

Prova, os últimos 3 testes na lista abaixo. O horrível nulo Out foi 32339,3792 milissegundos, mas espere - quanto mais rápido o lançamento foi para [void]? 34121.9251 ms?!? WTF? Estes são REAIS no meu sistema, a transmissão para VOID foi mais lenta. Que tal = $ null? 34217.685ms ..... ainda friggin MAIS LENTO! Portanto, como mostram os três últimos testes simples, o Out-Null é realmente MAIS RÁPIDO em muitos casos quando o pipeline já está em uso.

Então, por que isso? Simples. É e sempre foi 100% uma alucinação que a tubulação para Out-Null fosse mais lenta. É, no entanto, que TUBAR PARA QUALQUER COISA é mais lento, e nós já não sabíamos disso através da lógica básica? Podemos simplesmente não saber QUANTO MAIS LENTO, mas esses testes certamente contam uma história sobre o custo do uso do pipeline, se você puder evitá-lo. E, na verdade, não estávamos 100% errados, porque há um número muito PEQUENO de cenários verdadeiros em que nulo externo é ruim. Quando? Ao adicionar Out-Null, adicione a atividade do pipeline ONLY. Em outras palavras ... o motivo de um comando simples como $ (1..1000) | Out-Null como mostrado acima mostrou verdadeiro.

Se você simplesmente adicionar um pipe adicional ao Out-String em todos os testes acima, os #s mudam radicalmente (ou apenas cola os abaixo) e, como você pode ver por si mesmo, o Out-Null se torna MAIS RÁPIDO em muitos casos:

$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
Collin Chaffin
fonte
+1 por fazer esse ponto importante. De fato, não há nada obrigando alguém a usar Out-Nullum pipeline; portanto, a melhor maneira de mostrar a sobrecarga de um pipeline é invocá-lo Out-Nullcom e sem ele. No meu sistema, para 10.000 iterações, recebo 0,576 segundos Out-Null -InputObject $GetProcesscontra 5,656 segundos (quase 10 vezes mais lento) por $GetProcess | Out-Null.
BACON
Olá Collin, obrigado pela sua resposta. Eu executei suas amostras, mas em todos os lotes [void]e $nullainda obtive um desempenho melhor que | Out-Null. Entendo que isso se deve ao pipeline e o delta diminui com os lotes posteriores, mas na minha máquina Out-Nullnão há um desempenho mais rápido em nenhum dos lotes.
Hinek
Mas é claro que tenho que concordar com aqueles que argumentam que o desempenho não é tudo e a legibilidade também deve levar em consideração.
Hinek
@Hinek Seu comentário sugere que você não entendeu o que Collin estava dizendo. Sim, [void]e $nullterá um desempenho melhor do que | Out-Null- por causa do |. Tente Out-Null -InputObject (expression)para comparação.
Jonathan Gilbert
19

Há também o Out-Nullcmdlet, que você pode usar em um pipeline, por exemplo Add-Item | Out-Null.

Página de manual para 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".
Ocaso Protal
fonte
O que você acha dos resultados do pequeno teste de benchmark na resposta de Jason?
Hinek
Muito interessante, especialmente a enorme diferença entre Out-Null e os outros métodos. Eu acho que vou mudar para [void]mesmo que a solução Out-Null pareça mais "powerhellish".
Ocaso Protal 21/03
Ainda não tenho certeza, qual é o meu caminho preferido. Mesmo essa enorme diferença parece dificilmente significativa, como stej já comentou.
Hinek 22/03
2
@Hinek [void]parece muito claro (embora não seja tão potente quanto eu disse), você verá no início da linha que não há saída nessa linha. Portanto, esta é outra vantagem, e se você fizer um Out-Null em grande loop, o desempenho poderia ser um problema;)
Ocaso Protal
11

Eu consideraria usar algo como:

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

A saída de $a.Addnão é retornada - isso vale para todas as $a.Addchamadas de método. Caso contrário, você precisará [void]pré- anexar antes de cada chamada.

Em casos simples, eu concordaria [void]$a.Addporque é bastante claro que a saída não será usada e será descartada.

stej
fonte
2

Pessoalmente, uso ... | Out-Nullporque, como outros comentaram, essa parece ser a abordagem mais "PowerShellish" em comparação com ... > $nulle [void] .... $null = ...está explorando uma variável automática específica e pode ser fácil ignorar, enquanto os outros métodos tornam óbvio com sintaxe adicional que você pretende descartar a saída de uma expressão. Porque ... | Out-Nulle ... > $nullno final da expressão, acho que eles se comunicam efetivamente "pegue tudo o que fizemos até esse ponto e jogue fora", além de poder comentá-los com mais facilidade para fins de depuração (por exemplo ... # | Out-Null), em comparação com colocar $null =ou [void] antes a expressão para determinar o que acontece após a execução.

Porém, vejamos uma referência diferente: não a quantidade de tempo que leva para executar cada opção, mas a quantidade de tempo que leva para descobrir o que cada opção faz . Tendo trabalhado em ambientes com colegas que não tinham experiência com o PowerShell ou mesmo com scripts, eu costumo tentar escrever meus scripts de uma maneira que alguém que, anos depois, talvez nem entenda o idioma que está procurando, possa ter um chance de descobrir o que está fazendo, pois eles podem estar em uma posição de precisar apoiá-lo ou substituí-lo. Isso nunca me ocorreu como uma razão para usar um método sobre os outros até agora, mas imagine que você está nessa posição e usa o helpcomando ou seu mecanismo de pesquisa favorito para tentar descobrir o queOut-Nullfaz. Você obtém um resultado útil imediatamente, certo? Agora tente fazer o mesmo com [void]e $null =. Não é tão fácil, não é?

É verdade que suprimir a saída de um valor é um detalhe bem menor comparado à compreensão da lógica geral de um script, e você só pode tentar "emburrecer" seu código muito antes de trocar sua capacidade de escrever um bom código para um script. capacidade do novato de ler ... código não tão bom. Meu argumento é que é possível que alguns que são fluentes no PowerShell nem estejam familiarizados com [void], $null =etc., e apenas porque eles podem ser executados mais rapidamente ou levar menos pressionamentos de tecla para digitar, não significa que sejam a melhor maneira de fazer isso. o que você está tentando fazer, e apenas porque uma linguagem fornece uma sintaxe peculiar, não significa que você deve usá-la em vez de algo mais claro e mais conhecido. *

* Presumo que isso Out-Nullseja claro e conhecido, o que não sei ser $true. Qualquer opção que você achar mais clara e acessível aos futuros leitores e editores do seu código (inclusive você), independentemente do tempo de digitação ou de execução, é a opção que recomendo que você use.

BACON
fonte