2

起初我对 PowerShell ScriptBlock 感到很兴奋,但最近我对它在块内执行排序感到困惑。例如:

$test_block = {
  write-host "show 1"
  ps
  write-host "show 2"
  Get-Date
}

调用 $test_block.Invoke() 的输出:

show 1
show 2
<result of command 'ps'>
<result of command 'get-date'>

输出东西的命令会先运行吗?

4

2 回答 2

3

这种行为是因为write-host没有将输出放在管道上。其他命令放置在管道上,因此在函数(调用)返回之前不会输出到屏幕。

要获得我相信您所期望的行为,请改用write-output,然后所有命令的结果将在管道中返回。

$test_block = {
  write-output "show 1"
  ps
  write-output "show 2"
  Get-Date
}

$test_block.Invoke()
于 2013-10-28T09:01:59.060 回答
2

为了补充大卫马丁的有用答案

实际上,Write-Host它用于直接写入主机,即基于控制台窗口的 PowerShell 会话中的控制台(终端)。也就是说,Write-Host故意绕过PowerShell 的成功输出流,命令和表达式隐含地将其数据发送到该输出流。

Write-Output是写入成功输出流的 cmdlet,但很少需要显式使用它;你可以直接使用"show 1""show 2" 原样

Write-Host用于创建用户界面(打印(彩色)消息以通知用户),而不是用于输出数据

有关更多信息,请参阅此答案的底部。

脚本块 ( { ... }) 通常&参数(解析)模式(如 cmdlet 和外部程序)中使用调用运算符调用,而不是通过它们的方法调用,这允许更熟悉的语法;例如:.Invoke()

  • & $test_block而不是$test_block.Invoke()
  • 带参数:& $test_block arg1 ...而不是$test_block.Invoke(arg1, ...)

也许更重要的是,使用这种基于运算符的调用语法(& { ... } .... { ... } ...)具有以下优点

  • 保留了正常的流式语义,这意味着成功输出(也)在脚本块生成时从脚本块发出,而在一个实例中.Invoke() 首先收集所有成功输出,然后返回 - 相比之下,输出直接到两种情况下都显示[System.Collections.ObjectModel.Collection[psobject]]Write-Host

    • 作为一个有益的副作用,您的特定输出排序问题消失了,但请注意,在PSv5+ 中通常仍然可能存在输出排序问题,尽管其原因无关:对于没有预定义格式数据的输出类型的隐式表格输出(隐含)是异步的,以确定合适的列宽(您的特定代码恰好只使用具有预定义格式数据的 cmdlet)。Format-Table

    • 有关更多信息,请参阅此答案;一个简单的复制:

       [pscustomobject] @{ foo = 1 }; Write-Host 'Should print after, but prints first.'
      
  • 允许您传递命名参数(例如-Foo Bar),而.Invoke()仅支持位置(未命名)参数(例如Bar)。

  • 保留了脚本终止错误的正常语义:

    # OK: & throw aborts the entire script; 'after' never prints.
    & { throw 'fatal' }; 'after'
    
    # !! .Invoke() "eats" the script-terminating error and
    # !! effectively converts it to a *statement*-terminating one.
    # !! Therefore, execution continues, and 'after' prints.
    { throw 'fatal' }.Invoke(); 'after'
    
  • 此外,使用基于操作符的调用为您提供了使用.源操作符代替 的选项&,以便直接在调用者的范围内运行脚本块,而.Invoke()方法调用&范围内运行(如):

    # Dot-sourcing the script block runs it in the caller's scope.      
    . { $foo='bar' }; $foo # -> 'bar'
    

使用.Invoke()最好仅限于 PowerShell SDK 项目(通常基于 C#,不能选择使用 PowerShell 运算符)。

于 2019-12-06T21:44:17.063 回答