Select-Object -First влияет на предыдущий командлет в конвейере - PullRequest
1 голос
/ 21 марта 2020

PowerShell Strongly Encouraged Development Guidelines, что должны использовать командлеты для середины конвейера , но я подозреваю, что это невозможно для параметра -Last для Select-Object. Просто потому, что вы не можете определить последнюю запись заранее. Другими словами: вам нужно будет дождаться окончания входного потока sh, пока вы не определите последнюю запись.
Чтобы доказать это, я написал небольшой скрипт:

$Data = 1..5 | ForEach-Object {[pscustomobject]@{Index = "$_"}}

$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object -Last 5 | ForEach-Object { Write-Host 'After' $_.Index }

и сравнил это на Select-Object *:

$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object * | ForEach-Object { Write-Host 'After' $_.Index }

С результатами (справа: Select-Object -Last 5, слева: Select-Object *):

-Last 5  *
-------  -
Before 1 Before 1
Before 2 After 1
Before 3 Before 2
Before 4 After 2
Before 5 Before 3
After 1  After 3
After 2  Before 4
After 3  After 4
After 4  Before 5
After 5  After 5

Несмотря на то, что это не задокументировано, я думаю, что могу Исходя из этого, сделайте вывод, что параметр -Last действительно душит конвейер.
Это не имеет большого значения, но я также проверил его по параметру -First и получил некоторые тревожные результаты. Чтобы лучше это показать, я выбираю не все объекты, а только **-First 2**:

$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object -First 2 | ForEach-Object { Write-Host 'After' $_.Index }

Before 1
After 1
Before 2
After 2

Обратите внимание, что с параметром -First 2 не только следующий командлет отображает два объекта, но и предыдущий командлет показывает только два объекта .

Очевидно, параметр -First ссылается непосредственно на объект предыдущего командлета, который отличается от, например, использования -Last 2 параметр:

$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object -Last 2 | ForEach-Object { Write-Host 'After' $_.Index }

Before 1
Before 2
Before 3
Before 4
Before 5
After 4
After 5

Это также происходит при использовании Out-Host вместо командлета Write-Host или отправке результатов в переменную, например:

$Before = ""; $After = ""
$Data | ForEach-Object { $Before += $_.Index; $_ } | Select-Object -First 2 | ForEach-Object { $After += $_.Index }
$Before
$After

Отображается как на Windows Powershell (5.1.18362.628), так и на PowerShell Core (7.0.0).
Это ошибка?

1 Ответ

3 голосов
/ 21 марта 2020

Select-Object влияет на вышестоящие команды путем обмана

Это может звучать как шутка, но это не так.

Для оптимизации производительности конвейерной потоковой передачи Select-Object использует трюк, недоступный обычному пользователю, разрабатывающему Cmdlet - он выдает StopUpstreamCommandsException.

Один раз Пойманный, среда выполнения (косвенно) вызывает StopProcessing() для всех предыдущих команд, но не обрабатывает это как завершающее событие ошибки, позволяя нижестоящим командлетам продолжить выполнение.

Это очень полезно, когда у вас есть медленная или вычислительно тяжелая команда в начале конвейера:

# this will only take ~3 seconds to return with the StopUpstreamCommand behavior
# but would have incurred 8 extra seconds of "waiting to discard" otherwise
Measure-Command {
  1..5 |ForEach-Object { Start-Sleep -Seconds 1; $_ } |Select-Object -First 3
}
...