Как определить, будет ли Write-Host работать на текущем хосте - PullRequest
4 голосов
/ 30 января 2020

Существует ли какой-либо разумный, надежный контракт, который определяет, поддерживается ли Write-Host в данной реализации хоста PowerShell, в сценарии, который может выполняться на любой разумной реализации хоста?

(Предположим, я понимаю Разница между Write-Host и Write-Output / Write-Verbose и тем, что я определенно хочу * семантику Write-Host, если она поддерживается, для этого специфического c удобочитаемого текста.)

Я думал о попытке опросить переменную $Host, или $Host.UI / $Host.UI.RawUI, но единственные подходящие различия, которые я замечаю:

  • в $Host.Name:

    • В командной строке Windows powershell.exe $Host.Name = 'ConsoleHost'
    • в ISE $Host.Name = 'Windows PowerShell ISE Host'
    • SQL Шаги задания агента сервера имеют $Host.Name = 'Default Host'
    • У меня нет из не Windows установленных версий, но я ожидаю, что они отличаются
  • в $Host.UI.RawUI:

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

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

Фон

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

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

  1. с помощью шага CmdExe c, который представляет собой простое выполнение командной строки, где текст команды шага задания представляет собой исполняемый файл и его аргументы, поэтому вы вызываете powershell .exe исполняемый файл. Захваченный вывод - это стандартный вывод процесса:

    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'), вы only получаете поток ошибок в журнале и, если он успешен, вы получаете только выходной поток в журнале.

Кроме того, встроенная реализация 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 Сервер 2019 (возможно, более ранний), по-видимому, Write-Host больше не генерирует исключение во встроенном SQL узле PS Agent сервера, а вместо этого - неоперативный объект, который ничего не генерирует ни для выходных данных, ни для потоков ошибок. Поскольку исключения не существует, функция Write-Message моего сценария больше не может надежно определять, следует ли использовать Write-Host или StringBuilder.AppendLine.

Обходной путь c для SQL Заданий агента сервера заключается в использовании более зрелого типа шага CmdExe c (где Write-Output и Write-Host оба захватываются как стандартный вывод), но я предпочитаю Тип шага PowerShell для (среди прочих причин) его способности надежно разделять команду по нескольким строкам, поэтому я стремлюсь увидеть, существует ли более целостный c подход на основе PowerShell для решения вопроса о том, является ли Write-Host делает что-нибудь полезное для хоста, в котором я нахожусь.

...