因此,在 Powershell 中,如果您写入输出一些行然后抛出错误,您会期望获得错误发生之前发生的输出。所以如果你执行这个:
Write-Output ("test output 1")
Write-Output ("test output 2")
Write-Output ("test output 3")
Write-Output ("test output 4")
Write-Output ("test output 5")
throw "pretend error"
Run Code Online (Sandbox Code Playgroud)
您将得到预期的结果:
test output 1
test output 2
test output 3
test output 4
test output 5
pretend error
At C:\Powershell\Test.ps1:7 char:1
+ throw "pretend error"
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (pretend error:String) [], RuntimeException
+ FullyQualifiedErrorId : pretend error
Run Code Online (Sandbox Code Playgroud)
但是,如果您在数组中写入输出一堆对象,然后抛出错误,则数组的写入输出(以及之后的任何内容)永远不会出现。所以如果你运行这个:
Write-Output ("test output 1")
Write-Output ("test output 2")
Write-Output ("test output 3")
Write-Output ("test output 4")
Write-Output ("test output 5")
$testlist = @()
$count = 1
While($count -lt 6)
{
$testobj = new-object psobject -prop @{Name="array object $count"}
$testlist += $testobj
$count +=1
}
Write-Output $testlist
Write-Output ("test output 6")
Write-Output ("test output 7")
throw "pretend error"
Run Code Online (Sandbox Code Playgroud)
...“测试输出 5”之后您不会得到任何输出。就好像输出数组会使所有内容都得到缓冲,然后错误会丢失缓冲区。与 Powershell 使用带有数组的管道的方式有关吗?
所以我的问题是:我需要做什么才能确保写入输出不会因后续错误而丢失。这使得日志记录变得非常困难。
这是在 Powershell 5.1 中
看起来您看到了有问题的 300 毫秒延迟的一个非常不幸的副作用,该延迟是在 PowerShell v5 中隐式使用引入的Format-Table,以便更好地计算合适的列宽度,本答案对此进行了详细说明,并在GitHub 问题中进行了讨论# 4594 ; 但是,您的症状非常严重,需要提交新的错误报告:GitHub issues #13985。
因为您的$testlist数组包含的对象的属性少于4属性(即在您的情况下只有一个),并且因为对象的类型 ( [pscustomobject]) 没有与其关联的格式化数据,所以 PowerShell隐式使用Format-Table输出格式化。
当throw遇到该语句时,300 毫秒的延迟尚未过去,并且由于throw终止运行空间[1],因此永远不会显示Format-Table输出(以及后续输出)。Write-Output
不完美的解决方法是强制输出同步,这可以通过以下三种方式之一来完成:
Use ,同步Out-Host使用默认输出行为,但这也意味着从 PowerShell 内部您将无法捕获或重定向输出;然而,当从外部通过 PowerShell 的 CLI 调用时,输出会发送到 stdout 并且也可以被捕获。Out-Host
Format-Table 明确使用. 这也适用于显示,但将数据的输出更改为格式化指令的不相关对象,PowerShell 本身将其转换为通常的丰富显示格式,但如果您想捕获输出,这些是无用的 - 您的原始数据是丢失的。
如果您不想允许从 PowerShell 内部捕获原始对象的格式化字符串表示形式Out-String,则可以使用,它将格式化表示形式输出为单个多行字符串(不幸的是,带有额外的尾随换行符);当从 PowerShell 外部调用时,效果本质上与调用 相同Out-Host。
请注意,在您的情况下,解决方法只需应用于语句$testlist:
# Or ... | Out-Host or ... | Format-Table - see comments above.
$testlist | Out-String
Run Code Online (Sandbox Code Playgroud)
还有一种替代方案,但更晦涩难懂,它依赖于使用Start-Sleep等待至少 300 毫秒。并在隐式调用之后至少再输出一个对象Format-Table,如这个简化示例所示:
'before'
[pscustomobject] @{ foo = 1 }
Start-Sleep -Milliseconds 300 # wait for implicit Format-Table
'after' # force output of the table by outputting at least one more object
throw "error"
Run Code Online (Sandbox Code Playgroud)
此解决方法的优点是您的原始对象以这种方式保留在输出中,这将允许您从 PowerShell 内部捕获它们,例如:
$output = try { ./someScript.ps1 } catch { Write-Error $_ }
最后,你的代码可以被简化;把它们放在一起:
# Use *implicit* output - no need for Write-Output
# If you do use Write-Output: separate the arguments with *spaces*,
# don't put (...) around the argument list.
"test output 1"
# ...
# Implicitly capture the foreach loop output in an array.
# This is much more efficient than using += to "extend" an array
# in a loop (which requires creating a *new* array every time).
[array] $testlist =
foreach ($count in 1..6) {
# Simpler and more efficient PSv3+ method for constructing
# custom objects.
[pscustomobject] @{ Name="array object $count" }
}
# Apply the workaround.
$testlist | Out-String
# Use implicit output again.
"test output 6"
"test output 7"
throw "pretend error"
Run Code Online (Sandbox Code Playgroud)
[1] 也就是说,throw生成一个运行空间终止(脚本终止)错误,该错误立即中止整个执行 - 与语句终止错误不同,后者仅中止手头的语句,例如在 . NET方法调用;$ErrorActionPreference您可以通过将首选项变量设置为'Stop'- 请参阅此答案,将所有语句终止错误以及所有非终止错误转换为运行空间终止错误。
| 归档时间: |
|
| 查看次数: |
679 次 |
| 最近记录: |