dav*_*ave 4 powershell scriptblock
起初我对 PowerShell ScriptBlock 很兴奋,但最近我对它在块内的执行顺序感到困惑。例如:
$test_block = {
write-host "show 1"
ps
write-host "show 2"
Get-Date
}
Run Code Online (Sandbox Code Playgroud)
调用 $test_block.Invoke() 的输出:
show 1
show 2
<result of command 'ps'>
<result of command 'get-date'>
Run Code Online (Sandbox Code Playgroud)
输出内容的命令会先运行吗?
此行为是因为write-host不会将输出放到管道上。其他命令放置在管道上,因此在函数(调用)返回之前不会输出到屏幕。
为了获得我认为您期望的行为,请改用write-output,所有命令的结果将在管道中返回。
$test_block = {
write-output "show 1"
ps
write-output "show 2"
Get-Date
}
$test_block.Invoke()
Run Code Online (Sandbox Code Playgroud)
为了补充大卫·马丁的有用答案:
避免Write-Host(这通常是错误的工具使用)而支持Write-Output,即输出到成功输出流- 而不是使用[1]打印到显示器- 可以解决您眼前的问题。Write-Host
但是,您可以通过使用&调用运算符而不是.Invoke()方法来避免该问题:
# Invokes the script block and *streams* its output.
& $test_block
Run Code Online (Sandbox Code Playgroud)
&是更好的选择,而不仅仅是为了避免.Invoke()“collect-all-success-output-stream-first”行为 - 请参阅下一节。作为旁白:
这个答案描述了具有类似症状的常见问题(成功输出(管道输出)相对于其他流出现无序),尽管它在技术上不相关,并且也发生在&:
# !! The Write-Host output prints FIRST, due to implicit, asynchronous
# !! table formatting of the [pscustomobject] instance.
& { [pscustomobject] @{ Foo = 'Bar' }; Write-Host 'Why do I print first?' }
Run Code Online (Sandbox Code Playgroud)
&而非来调用脚本块 ( ):.Invoke(){ ... }脚本块( { ... }) 通常&在参数(解析)模式(如 cmdlet 和外部程序)中使用调用运算符进行调用,而不是通过其方法来调用,这允许使用更熟悉的语法;例如:.Invoke()
& $test_block而不是$test_block.Invoke()& $test_block arg1 ...而不是$test_block.Invoke(arg1, ...)也许更重要的是,使用这种基于运算符的调用语法(& { ... } ...或. { ... } ...)具有以下优点:
它保留正常的流语义,这意味着成功输出(也)是在生成脚本块时从脚本块发出的,而在一个实例中,.Invoke() 首先收集所有成功输出,然后返回 - 相比之下,输出直接发送到两种情况下均显示。[System.Collections.ObjectModel.Collection[psobject]]Write-Host
作为一个有益的副作用,您的特定输出排序问题消失了,但请注意,在PSv5+ 中,通常仍然存在输出排序问题,尽管其原因无关:没有预定义格式数据的输出类型的隐式表格输出(隐式Format-Table)是异步的,以确定合适的列宽度(您的特定代码恰好仅使用具有预定义格式数据的 cmdlet)。
请参阅此答案以获取更多信息;一个简单的重现:
[pscustomobject] @{ foo = 1 }; Write-Host 'Should print after, but prints first.'
Run Code Online (Sandbox Code Playgroud)
它允许您传递命名参数(例如-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'
Run Code Online (Sandbox Code Playgroud)
此外,使用基于运算符的调用使您可以选择使用.,即点源运算符来代替&,以便直接在调用者的作用域中运行脚本块,而.Invoke()方法调用仅在子作用域中运行(&就像):
# Dot-sourcing the script block runs it in the caller's scope.
. { $foo='bar' }; $foo # -> 'bar'
Run Code Online (Sandbox Code Playgroud)
的使用.Invoke()最好仅限于PowerShell SDK项目(通常基于 C#,其中不能使用 PowerShell 运算符)。
[1] 从技术上讲,由于 PowerShell v5Write-Host输出到信息流(流编号6),但是默认情况下会打印到显示器,同时在管道和重定向中被忽略。请参阅此答案Write-Host以了解和的并置Write-Output,以及为什么通常需要注意后者。
| 归档时间: |
|
| 查看次数: |
1785 次 |
| 最近记录: |