Порядок выполнения команд внутри блока сценариев PowerShell
Сначала я был в восторге от PowerShell ScriptBlock, но недавно меня смутило выполнение порядка внутри блоков. Например:
$test_block = {
write-host "show 1"
ps
write-host "show 2"
Get-Date
}
Вывод путем вызова $test_block.Invoke():
show 1
show 2
<result of command 'ps'>
<result of command 'get-date'>
Команды, которые выводят что-то, запускаются первыми?
1 ответ
Это происходит потому, что write-host не помещает вывод в конвейер. Другие команды размещаются на конвейере, поэтому не выводятся на экран до тех пор, пока не вернется функция (invoke).
Чтобы получить поведение, которое, как я полагаю, вы ожидали, используйте вместо этого запись-вывод, результаты всех команд будут затем возвращаться в конвейер.
$test_block = {
write-output "show 1"
ps
write-output "show 2"
Get-Date
}
$test_block.Invoke()
Чтобы дополнить полезный ответ Дэвида Мартина:
На самом деле, Write-Host
предназначен для записи непосредственно на хост, который является консолью (терминалом) в сеансе PowerShell на основе окна консоли. То есть,Write-Host
намеренно обходит поток вывода успеха PowerShell, которому команды и выражения неявно отправляют свои данные.
Write-Output
это командлет, который записывает в поток вывода успешных результатов, но его явное использование редко требуется; ты мог бы просто использовать"show 1"
а также "show 2"
как есть.
Write-Host
предназначен для создания пользовательских интерфейсов (печати (цветных) сообщений для информирования пользователя), а не для вывода данных.
См. Дополнительную информацию в нижнем разделе этого ответа.
Блоки скрипта ({ ... }
) обычно вызываются с помощью &
, оператор вызова, а не через их .Invoke()
метод.
.Invoke()
действительно, сначала собирает все данные потока успешного вывода, прежде чем выводить их при возврате из вызова метода, тогда как Write-Host
звонки немедленно.
Однако это поведение не относится к вызову с помощью&
:
Звонки с&
- а также прямой вызов команд в последовательности - также немедленно выводите объекты потока успеха, так что вы получите ожидаемый порядок вывода.
$test_block = {
write-host "show 1"
ps
write-host "show 2"
Get-Date
}
# Use of & to call the script block results in the expected output order.
& $test_block
# show 1
# <ps output>
# show 2
# <get-date output>
Однако в PSv5+ все еще может быть проблема с упорядочением вывода, хотя ее причина не связана: неявный табличный вывод (подразумеваемыйFormat-Table
) для типов вывода без предопределенного формата данных является асинхронным, чтобы определить подходящую ширину столбца.
См. Следующие примеры и этот ответ для получения дополнительной информации.
# OK: Output ordering as expected:
# Get-Item outputs a System.IO.DirectoryInfo instance for which
# format data is predefined.
PS> Write-Host '------- 1'; Get-Item /; Write-Host '------- 2'
------- 1
Directory:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--hs- 12/5/2019 3:34 PM C:\
------- 2
# !! OUT OF SEQUENCE, because Select-Object creates [pscustomobject] instances
# !! for which no format data is predefined, and custom object with 4 or fewer
# !! properties triggers implicit Format-Table display.
PS> Write-Host '------- 1'; Get-Item / | Select-Object FullName; Write-Host '------- 2'
------- 1
------- 2
FullName
--------
C:\
Вы можете решить эту проблему, направив команду вне последовательности в Out-Host
, но обратите внимание, что это предотвращает дальнейшую программную обработку его вывода:
# Using Out-Host fixes the output-ordering problem, but prints
# the data to the host (display) only.
PS> Write-Host '------- 1'; Get-Item / | Select-Object FullName | Out-Host; Write-Host '------- 2'
------- 1
FullName
--------
/
------- 2
См. Этот ответ для получения потенциального, но неоптимального решения, которое не требуетOut-Host
и вместо этого отправляет строки для отображения в поток вывода успеха, достигая раскраски с помощью встроенных управляющих последовательностей VT (виртуального терминала).