Какой лучший (более чистый) способ игнорировать вывод в PowerShell? [закрыто]

134

Допустим, у вас есть метод или командлет, который что-то возвращает, но вы не хотите его использовать и не хотите выводить его. Я нашел эти два способа:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

Что ты используешь? Какой подход лучше / чище? Зачем?

Hinek
источник
1
вроде [void] версия ... еще не была для меня, но я постараюсь ее запомнить.
Массив

Ответы:

181

Я только что провел несколько тестов четырех известных мне вариантов.

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накладные расходы > $nullсоставляют около 60%, а накладные расходы - около 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
источник
1
я обычно использую VOID для вещей, которые являются частью языковой строки, и out-null, если это уже большой длинный конвейер
klumsy
25
Out-Nullвозможно накладные расходы. Но .. если передать один объект на Out-Null0,076 миллисекунды, imho, он все еще отлично подходит для языка сценариев :)
stej
1
Я немного возился с этим, и я получаю лучшую производительность по сравнению с [void] и> $ null, используя Out-Null -InputObject ($ (1..1000) |? {$ _ -Is [int]}).
tomohulk
1
Хороший момент, который, кажется, не имеет заметных накладных расходов.
JasonMArcher
5
Хотя всегда полезно иметь тесты для сравнения различных способов достижения чего-либо, давайте не будем упускать из виду и другой вывод, который можно сделать здесь: если вам не нужно отбрасывать значения в сотни тысяч раз, различия в производительности незначительны. И если миллисекунды здесь или там действительно важны для вас, вам, вероятно, вообще не следует использовать PowerShell. Без какого-либо суждения о том, как параметры оцениваются в этих отношениях, не обязательно плохо жертвовать скоростью ради других качеств, таких как читаемость и выражение намерений программиста.
BACON
22

Я понимаю, что это старый поток, но для тех, кто принимает принятый ответ @JasonMArcher выше как факт, я удивлен, что он не был исправлен, многие из нас знали в течение многих лет, что на самом деле ТРУБОПРОВОД добавляет задержку и НИЧЕГО не связано с тем, Это Out-Null или нет. Фактически, если вы запустите приведенные ниже тесты, вы быстро увидите, что те же самые «более быстрые» преобразования в [void] и $ void =, которые мы годами использовали, думая, что это было быстрее, на самом деле ПРОСТО КАК МЕДЛЕННО и на самом деле ОЧЕНЬ МЕДЛЕННО, когда Вы добавляете ЛЮБОЙ конвейер вообще. Другими словами, как только вы переходите к чему-либо, все правило не использовать out-null попадает в корзину.

Доказательство, последние 3 теста в списке ниже. Ужасный Out-null был 32339,3792 миллисекунды, но подождите - насколько быстрее было приведение к [void]? 34121,9251 мс?!? WTF? Это НАСТОЯЩИЕ # в моей системе, преобразование в VOID было на самом деле МЕДЛЕННЫМ. Как насчет = $ null? 34217,685мс ..... еще чертовски МЕДЛЕННО! Итак, как показывают последние три простых теста, Out-Null на самом деле БЫСТРЕЕ во многих случаях, когда конвейер уже используется.

Итак, почему это? Просто. Это и всегда было на 100% галлюцинацией, что передача в Out-Null была медленнее. Тем не менее, ТРУБА К ЧЕМУ-ЛИБО медленнее, и разве мы не знали, что это через базовую логику? Возможно, мы просто не знали НАМНОГО медленнее, но эти тесты наверняка расскажут о стоимости использования конвейера, если вы можете этого избежать. И мы на самом деле не были на 100% неправы, потому что есть очень МАЛЕНЬКОЕ количество истинных сценариев, где out-null - это зло. Когда? При добавлении Out-Null добавляется ТОЛЬКО действие конвейера. Другими словами .... причина в простой команде типа $ (1..1000) | Out-Null, как показано выше, показал истину.

Если вы просто добавите дополнительный канал к 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 итераций я получаю 0,576 секунды для Out-Null -InputObject $GetProcessпротив 5,656 секунды (почти в 10 раз медленнее) для $GetProcess | Out-Null.
BACON 05
Привет, Коллин, спасибо за ответ. Я пробовал ваши образцы, но во всех партиях [void]и по- $nullпрежнему выступал лучше, чем | Out-Null. Я понимаю, что это из-за конвейера, и дельта сокращается с более поздними партиями, но на моей машине Out-Nullне работает быстрее ни в одной из партий.
Хинек
Но, конечно, я должен согласиться с теми, кто утверждает, что производительность - это еще не все, и читабельность тоже должна учитываться.
Hinek
@Hinek Ваш комментарий предполагает, что вы упустили суть того, что говорил Коллин. Да [void]и $nullбудет работать лучше, чем | Out-Null- из-за |. Попробуй Out-Null -InputObject (expression)для сравнения.
Джонатан Гилберт,
19

Также существует Out-Nullкомандлет, который можно использовать, например, в конвейере 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".
Окасо Проталь
источник
Что вы думаете о результатах небольшого теста в ответе Джейсона?
Хинек
Очень интересно, особенно огромная разница между Out-Null и другими методами. Думаю, я перейду на [void]вариант, хотя решение Out-Null выглядит более "мощным".
Окасо Проталь
Я все еще не уверен, какой мой предпочтительный путь. Даже эта огромная разница кажется едва заметной, как уже прокомментировал stej.
Хинек
2
@Hinek [void]выглядит очень четко (хотя, как я уже сказал, не PowerShellish), вы увидите в начале строки, что в этой строке нет вывода. Так что это еще одно преимущество, и если вы делаете Out-Null в большом цикле, производительность может быть проблемой;)
Ocaso Protal
11

Я бы подумал об использовании чего-то вроде:

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потому что совершенно ясно, что вывод не будет использоваться и будет отброшен.

stej
источник
2

Лично я использую, ... | Out-Nullпотому что, как уже отмечали другие, это выглядит как более «PowerShellish» подход по сравнению с ... > $nullи [void] .... $null = ...использует определенную автоматическую переменную, и ее можно легко упустить из виду, тогда как другие методы делают очевидным с помощью дополнительного синтаксиса, что вы намереваетесь отбросить вывод выражения. Потому что ... | Out-Nullи ... > $nullв конце выражения, я думаю, они эффективно сообщают: «возьмите все, что мы сделали до этого момента, и выбросьте это», плюс вы можете комментировать их проще для целей отладки (например ... # | Out-Null), по сравнению с помещением $null =или [void] ранее выражение, чтобы определить, что происходит после его выполнения.

Давайте рассмотрим другой тест: не количество времени, которое требуется для выполнения каждого параметра, а количество времени, которое требуется для выполнения выяснить, что делает каждый вариант . Работая в среде с коллегами, которые не имели опыта работы с PowerShell или даже с написанием сценариев, я стараюсь писать свои сценарии таким образом, чтобы кто-то, кто придет через несколько лет и даже не поймет язык, на который они смотрят, может иметь шансы выяснить, что он делает, так как они могут поддержать или заменить его. До сих пор это никогда не приходило мне в голову как причина использовать один метод вместо других, но представьте, что вы находитесь в таком положении и используете helpкоманду или свою любимую поисковую систему, чтобы попытаться выяснить, чтоOut-Nullделает. Вы сразу получаете полезный результат, верно? Теперь попробуйте сделать то же самое с [void]и $null =. Не так просто?

Конечно, подавление вывода значения является довольно незначительной деталью по сравнению с пониманием общей логики скрипта, и вы можете только попытаться «замять» код настолько, прежде чем торгуете своей способностью написать хороший код для способность новичка читать ... не очень хороший код. Суть в том, что это возможно , что некоторые , кто владеет PowerShell даже не знакомы с [void], $null =и т.д., и только потому , что те могут выполняться быстрее или принимать меньше нажатий клавиш типа, не означает , что они лучший способ сделать то, что вы пытаетесь сделать, и то, что язык дает вам необычный синтаксис, не означает, что вы должны использовать его вместо чего-то более ясного и известного. *

* Я предполагаю, что Out-Nullэто ясно и хорошо известно, чего я не знаю $true. Какой бы вариант вы ни считали наиболее ясным и доступным для будущих читателей и редакторов вашего кода (включая вас самих), независимо от времени ввода или выполнения, я рекомендую вам использовать именно этот вариант.

БЕКОН
источник