Почему 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 не работает внутри метода класса?


person Samselvaprabu    schedule 11.10.2018    source источник


Ответы (3)


Из ссылки документы:

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

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

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

person marsze    schedule 11.10.2018

Чтобы добавить к отличный ответ marsze:

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

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

person Mathias R. Jessen    schedule 11.10.2018
comment
Хороший способ увидеть это. Явное указание возвращаемого типа само по себе сильно отличает их от обычных функций PowerShell. - person marsze; 11.10.2018

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

#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!

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

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

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

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

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