Как определить, будет ли Write-Host работать для текущего хоста
Есть ли какой-нибудь разумный, надежный контракт, который диктует, Write-Host
поддерживается в данной реализации хоста PowerShell в сценарии, который может быть запущен против любой разумной реализации хоста?
(Предположим, я понимаю разницу между Write-Host
а также Write-Output
/Write-Verbose
и что я определенно хочу Write-Host
семантика, если поддерживается, для этого конкретного текста, читаемого человеком.)
Я думал о том, чтобы попытаться допросить $Host
переменная, или $Host.UI
/$Host.UI.RawUI
но единственные существенные отличия, которые я замечаю:
в
$Host.Name
:- В командной строке Windows powershell.exe есть
$Host.Name = 'ConsoleHost'
- ISE имеет
$Host.Name = 'Windows PowerShell ISE Host'
- Шаги задания агента SQL Server имеют
$Host.Name = 'Default Host'
- У меня не установлена ни одна из версий, отличных от Windows, но я думаю, что они разные
- В командной строке Windows powershell.exe есть
в
$Host.UI.RawUI
:- Командная строка Windows powershell.exe возвращает значения для всех свойств
$Host.UI.RawUI
- ISE не возвращает значения (или
$null
) для некоторых свойств$Host.UI.RawUI
, например$Host.UI.RawUI.CursorSize
- Шаги задания агента SQL Server не возвращают значений для всех
$Host.UI.RawUI
- Опять же, я не могу проверить ни одну из других платформ
- Командная строка Windows powershell.exe возвращает значения для всех свойств
Ведение списка $Host.Name
ценности, поддерживающие Write-Host
похоже, что это было бы немного обременительным, особенно с учетом того, что PowerShell теперь кроссплатформенный. Я разумно хотел бы, чтобы скрипт мог вызываться с любого хоста и просто делать правильные вещи.
Задний план
Я написал сценарий, который можно разумно запустить из командной строки PowerShell, из ISE или из задания агента SQL Server. Вывод этого сценария полностью текстовый, для чтения человеком. При запуске из командной строки или ISE вывод раскрашивается с использованиемWrite-Host
.
Задания SQL Server можно настроить двумя разными способами, и оба поддерживают запись вывода в средство просмотра журнала агента SQL Server:
через шаг CmdExec, который представляет собой простое выполнение в командной строке, где текст команды шага задания представляет собой исполняемый файл и его аргументы, поэтому вы вызываете исполняемый файл powershell.exe. Захваченный вывод - это stdout/sterr процесса:
powershell.exe -Command x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
через шаг PowerShell, где текст команды шага задания представляет собой необработанный скрипт PS, интерпретируемый его собственной встроенной реализацией хоста PowerShell. Захваченный вывод - это то, что написано через
Write-Output
илиWrite-Error
:#whatever Do-WhateverPowershellCommandYouWant x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
Из-за некоторых других недостатков реализации хоста SQL Server я обнаружил, что вы можете выдавать вывод, используя либо Write-Output
или Write-Error
, но не то и другое одновременно. Если этап задания не выполняется (например, если выthrow
или Write-Error 'foo' -EA 'Stop'
), вы получаете только поток ошибок в журнале, а в случае успеха вы получаете только выходной поток в журнале.
Кроме того, встроенная реализация PS не поддерживает Write-Host
. По крайней мере, до SQL Server 2016,Write-Host
бросает System.Management.Automation.Host.HostException
с сообщением Команда, которая запрашивает пользователя, завершилась ошибкой, поскольку основная программа или тип команды не поддерживает взаимодействие с пользователем.
Пока что для поддержки всех моих вариантов использования я использовал настраиваемую функцию Write-Message
который, по сути, был настроен как (упрощенный):
$script:can_write_host = $true
$script:has_errors = $false
$script:message_stream = New-Object Text.StringBuilder
function Write-Message {
Param($message, [Switch]$iserror)
if ($script:can_write_host) {
$private:color = if ($iserror) { 'Red' } else { 'White' }
try { Write-Host $message -ForegroundColor $private:color }
catch [Management.Automation.Host.HostException] { $script:can_write_host = $false }
}
if (-not $script:can_write_host) {
$script:message_stream.AppendLine($message) | Out-Null
}
if ($iserror) { $script:has_errors = $true }
}
try {
<# MAIN SCRIPT BODY RUNS HERE #>
}
catch {
Write-Message -Message ("Unhandled error: " + ($_ | Format-List | Out-String)) -IsError
}
finally {
if (-not $script:can_write_host) {
if ($script:has_errors) { Write-Error ($script:message_stream.ToString()) -EA 'Stop' }
else { Write-Output ($script:message_stream.ToString()) }
}
}
Начиная с SQL Server 2019 (возможно, ранее), похоже, Write-Host
больше не генерирует исключение на узле PS встроенного агента SQL Server, а вместо этого является бездействующим, которое ничего не передает ни в поток вывода, ни в поток ошибок. Поскольку здесь нет исключений, мой сценарийWrite-Message
функция больше не может надежно определять, следует ли ей использовать Write-Host
или StringBuilder.AppendLine
.
Основным обходным путем для заданий агента SQL Server является использование более зрелого типа шага CmdExec (где Write-Output
а также Write-Host
оба фиксируются как стандартный вывод), но я предпочитаю тип шага PowerShell (среди прочего) из-за его способности надежно разделять команду на несколько строк, поэтому я очень хочу увидеть, существует ли более целостный подход на основе PowerShell. решить проблему Write-Host
делает что-нибудь полезное для хозяина, в котором я нахожусь.
2 ответа
Просто проверьте, является ли ваш хост UserInteractive или средой типа службы.
$script:can_write_host = [Environment]::UserInteractive
Еще один способ отслеживать вывод скрипта в реальном времени - поместить этот вывод в файл журнала, а затем отслеживать его в реальном времени с помощью trace32. Это всего лишь обходной путь, но он может сработать для вас.
Add-Content -Path "C:\Users\username\Documents\PS_log.log" -Value $variablewithvalue