10

我正在尝试使用 输出变量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

3 回答 3

13

文档

在类方法中,除了 return 语句中提到的对象之外,没有对象被发送到管道。代码没有意外输出到管道。

这与 PowerShell 函数处理输出的方式根本不同,所有内容都进入管道。

如果您只需要用于调试或其他什么的输出,您可以使用Write-HostWrite-Warning,它基本上只是写入控制台。

于 2018-10-11T10:36:48.737 回答
7

添加到marsze的出色答案

将方法签名 ( [string] sampleMethod()) 视为一种契约——您向用户保证,如果他们使用 0 个参数调用该方法,它将始终只返回一个[string]对象

在方法执行期间允许任意数量的Write-Output语句将违反该合同

于 2018-10-11T11:26:57.470 回答
3

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

这是一个相对 hacky 的解决方案。不过,据我所知,这是在 cmdlet 仍在运行时编写在静态方法中调用的 cmdlet 输出的唯一方法。write-host如果您无权访问您在类中调用的 cmdlet,则不能在方法调用的 cmdlet 内部使用。

不使用脚本块的示例:

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

还值得注意的是,第二种方法导致所有输出都显示在一行上。

您可能希望实际使用它的一个场景是,如果您正在编写一个使用命令行安装工具的脚本。安装使用您无法控制的 cmdlet,并且需要几分钟才能完成(例如使用巧克力安装软件)。这意味着如果 cmdlet 的进度发生更改(例如继续安装软件的依赖项),它无法将更改写入控制台,直到完全安装完成,从而使用户对当前正在发生的事情一无所知。

更新:在撰写本文时,我还遇到了许多关于脚本块内范围使用的问题,因为它们不共享创建它们的上下文的范围,只共享执行它们的范围。这在很大程度上使我在这里提到的许多内容无效,因为这意味着您不能引用该类的属性。

更新 2:除非您使用 GetNewClosure!

    static [ScriptBlock]bar(){
        #create a ScriptBlock that the must be executed outside
        $that = $this;
        return { $that.ClassVariable }.GetNewClosure();
    }
于 2019-11-19T13:30:02.277 回答