为什么Write-Output在PowerShell类方法中不起作用?

时间:2018-10-11 10:19:53

标签: powershell class powershell-v5.0 powershell-v5.1

我正在尝试使用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在类方法中不起作用,是否有任何特定原因?

3 个答案:

答案 0 :(得分:8)

来自docs

  

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

     

这与PowerShell函数的处理方式根本不同   输出,一切都进入管道。

如果您仅需要调试输出或其他任何输出,则可以使用Write-HostWrite-Warning等,它们基本上只是写入控制台。

答案 1 :(得分:3)

要添加到marsze's excellent answer

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

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

答案 2 :(得分:0)

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

这是一个相对棘手的解决方案。据我所知,这是在cmdlet仍在运行时写入在静态方法内部调用的cmdlet输出的唯一方法。如果您无权访问在类内部调用的cmdlet,则不能选择在方法调用的cmdlet中使用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!

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

您可能希望实际使用此脚本的情况是编写的脚本将使用命令行安装工具。该安装使用您无法控制的cmdlet,该cmdlet需要几分钟才能完成(例如,使用Chocolatey安装软件)。这意味着,如果cmdlet的进度发生了变化(例如转到安装软件的依赖项),则在完全安装完成之前,它无法将更改写入控制台,从而使用户无法了解当前发生的情况。

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

更新2: 除非您使用GetNewClosure!

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