Почему Write-Output не работает внутри метода класса PowerShell?

Я пытаюсь вывести переменные используя Write-Output, но он не работал внутри метода класса PowerShell. Write-Host работает. Смотрите пример кода ниже.

class sample {
  [string] sampleMethod() {
    $output = "Output message"
    try {
      Write-Output $output
      throw "error"
    }
    catch {
      Write-Output $output
      $_
    }
    return "err"
  }
}    

$obj = [sample]::new()
$obj.sampleMethod()

Есть ли конкретная причина, почему Write-Output не работает внутри метода класса?

4 ответа

Решение

Из документов:

В методах класса никакие объекты не отправляются в конвейер, кроме тех, которые упомянуты в операторе возврата. Там нет случайного вывода в конвейер из кода.

Это принципиально отличается от того, как функции PowerShell обрабатывают вывод, когда все идет к конвейеру.

Если вам нужен вывод только для отладки или чего-то еще, вы можете использовать Write-Host, Write-Warning и т.д., которые в основном просто пишут в консоль.

Чтобы добавить к превосходному ответу Марша:

Подумайте о методе подписи ([string] sampleMethod()) в качестве контракта - вы обещаете пользователю, что если он вызовет метод с 0 параметрами, он всегда вернет ровно один [string] объект.

Разрешение произвольного числа Write-Output заявления во время выполнения метода будут нарушать этот контракт!

В то время как запись выход не работает внутри метода класса, он делает работу если метод возвращает скрипт-блок, который затем выполняется снаружи, например, так:

#Cmdlet you can't edit that outputs whilst running
function foo {
    write-output "Beginning complex operation!";
    start-sleep 2;
    write-output "Important information you would rather not have to wait for!";
    start-sleep 2;
    write-output "Operation finished!";
}

class IsClass{
    static [ScriptBlock]bar(){
        #create a ScriptBlock that the must be executed outside
        return { foo };
    }
}

& $([IsClass]::bar());
<#Output:
Beginning complex operation!
[two second wait]
Important information you would rather not have to wait for!
[two second wait]
Operation finished!
#>

Это относительно хакерское решение. Однако, насколько мне известно, это единственный способ записать вывод командлетов, вызываемых внутри статического метода, когда командлет все еще выполняется. Использованиеwrite-host внутри командлета, который вызывает метод, не является вариантом, если у вас нет доступа к командлетам, которые вы вызываете внутри класса.

Пример без использования блоков скрипта:

#Cmdlet you can't edit that outputs whilst running
function foo {
    write-output "Beginning complex operation!";
    start-sleep 2;
    write-output "Important information you would rather not have to wait for!";
    start-sleep 2;
    write-output "Operation finished!";
}

#Class that uses the mentioned cmdlet
class IsClass{
    static [void]bar(){
        #Directly invoke the method
        write-host $(foo);
    }
}

[IsClass]::bar();
<#Output:
[Awkward 4 second pause]
Beginning complex operation! Important information you would rather not have to wait for! Operation finished!

Также стоит отметить, что второй метод приводит к тому, что весь вывод отображается в одной строке.

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

ОБНОВЛЕНИЕ: на момент написания этого я также столкнулся со многими проблемами, связанными с использованием области видимости внутри блоков сценариев, поскольку они не разделяют область действия контекста, в котором они были созданы, а только область, в которой они выполняются. Это в значительной степени делает недействительным многое из того, что я здесь упомянул, так как это означает, что вы не можете ссылаться на свойства класса.

ОБНОВЛЕНИЕ 2: ЕСЛИ вы не используете GetNewClosure!

    static [ScriptBlock]bar(){
        #create a ScriptBlock that the must be executed outside
        $that = $this;
        return { $that.ClassVariable }.GetNewClosure();
    }

В дополнение к другим ответам вы также можете передать вывод команды, например «Command», в Out-Host внутри метода класса. При этом используется тот же поток, что и Write-Host, чтобы выходные данные отображались на терминале.

         Command | Out-Host
Другие вопросы по тегам