Почему 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