Как определить, будет ли 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, но я думаю, что они разные
  • в $Host.UI.RawUI:

    • Командная строка Windows powershell.exe возвращает значения для всех свойств $Host.UI.RawUI
    • ISE не возвращает значения (или $null) для некоторых свойств $Host.UI.RawUI, например $Host.UI.RawUI.CursorSize
    • Шаги задания агента SQL Server не возвращают значений для всех $Host.UI.RawUI
    • Опять же, я не могу проверить ни одну из других платформ

Ведение списка $Host.Name ценности, поддерживающие Write-Hostпохоже, что это было бы немного обременительным, особенно с учетом того, что PowerShell теперь кроссплатформенный. Я разумно хотел бы, чтобы скрипт мог вызываться с любого хоста и просто делать правильные вещи.

Задний план

Я написал сценарий, который можно разумно запустить из командной строки PowerShell, из ISE или из задания агента SQL Server. Вывод этого сценария полностью текстовый, для чтения человеком. При запуске из командной строки или ISE вывод раскрашивается с использованиемWrite-Host.

Задания SQL Server можно настроить двумя разными способами, и оба поддерживают запись вывода в средство просмотра журнала агента SQL Server:

  1. через шаг CmdExec, который представляет собой простое выполнение в командной строке, где текст команды шага задания представляет собой исполняемый файл и его аргументы, поэтому вы вызываете исполняемый файл powershell.exe. Захваченный вывод - это stdout/sterr процесса:

    powershell.exe -Command x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
    
  2. через шаг 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
Другие вопросы по тегам