将 Write-Host
输出与输出对象、字符串和 PowerShell 听录混合是复杂的。 脚本与听录使用 PowerShell 管道的方式之间存在微妙的交互,这可能会产生意想不到的结果。
从脚本发出对象时,这些对象的格式由 Out-Default
处理。
但是,脚本完成并停止转录后,格式化可能会发生。 这意味着输出不会被转录。 字符串的处理方式不同。 有时字符串输出会进行格式化,但并不总是如此。 Write-Host
会立即写入主机进程。 Write-Output
是通过格式设置系统发送的。 将复杂对象的输出与对主机的写入相结合,使得难以预测脚本中记录的内容。
方案 1 - 在所有其他操作之后输出结构化对象
请考虑以下脚本及其输出:
PS> Get-Content scenario1.ps1
Start-Transcript scenario1.log -UseMinimalHeader
Write-Host '1'
Write-Output '2'
Get-Location
Write-Host '4'
Write-Output '5'
Stop-Transcript
PS> ./scenario1.ps1
Transcript started, output file is scenario1.log
1
2
4
Path
----
/Users/user1/src/projects/transcript
5
Transcript stopped, output file is /Users/user1/src/projects/transcript/scenario1.log
控制台的输出显示所需的输出,但未按预期顺序显示。
Write-Host 4
比 Get-Location
更早可见,因为 Write-Host
已针对直接写入主机进行优化。 听录中有一个代码,用于将输出复制到脚本文件和控制台。 然后,我们将 Get-Location
和 Write-Output 5
作为脚本的常规输出发送。
PS> Get-Content scenario1.log
**********************
PowerShell transcript start
Start time: 20191106114858
**********************
Transcript started, output file is s2
1
2
4
**********************
PowerShell transcript end
End time: 20191106114858
**********************
由于脚本退出前已关闭转录功能,因此不会在转录中显示。 对象已发送给管道中的下一个使用者。 在这种情况下是 Out-Default
,这是 PowerShell 自动插入的。 为了进一步使事情复杂化,字符串的输出也在格式设置系统中得到优化。 第一个 Write-Output 2
被发出并由脚本捕获。 但是,插入 Get-Location
对象会导致其输出被推送到需要实际格式设置的内容堆栈中,这会为可能还需要格式化的任何剩余对象设置一点状态。 这就是为什么第二个 Write-Output 5
不会被添加到记录中的原因。
场景 2 - 将对象发出操作移到开头
请考虑以下脚本及其输出:
PS> Get-Content scenario2.ps1
Start-Transcript scenario2.log -UseMinimalHeader
Get-Location
Write-Host '1'
Write-Output '2'
Get-Location
Write-Host '4'
Write-Output '5'
Stop-Transcript
PS> ./scenario2.ps1
Transcript started, output file is scenario2.log
1
4
Path
----
/Users/user1/src/projects/transcript
2
5
Transcript stopped, output file is /Users/user1/src/projects/transcript/scenario2.log
我们可以看到命令 Write-Host
发生在所有内容之前,然后对象开始出现。字符串 Write-Output
让对象在屏幕上呈现,但请注意记录仅包含由 Write-Host
生成的输出。 这是因为脚本关闭转录后,这些字符串对象通过管道传递给Out-Default
进行格式设置。
PS> Get-Content scenario2.log
**********************
PowerShell transcript start
Start time: 20220606094609
**********************
Transcript started, output file is s3
1
4
**********************
PowerShell transcript end
End time: 20220606094609
**********************
场景 3 - 在脚本末尾发出对象
对于此方案,复杂对象的输出位于脚本末尾。
PS> Get-Content scenario3.ps1
Start-Transcript scenario3.log -UseMinimalHeader
Write-Host '1'
Write-Output '2'
Write-Host '4'
Write-Output '5'
Get-Location
Stop-Transcript
PS> ./scenario3.ps1
Transcript started, output file is scenario3.log
1
2
4
5
Path
----
/Users/user1/src/projects/transcript
Transcript stopped, output file is /Users/user1/src/projects/transcript/scenario3.log
两者Write-Host
和Write-Output
的字符串输出都被记录在笔录中。 但是,听录停止后将出现 Get-Location
的输出。
**********************
PowerShell transcript start
Start time: 20220606100342
**********************
Transcript started, output file is scenario3.log
1
2
4
5
**********************
PowerShell transcript end
End time: 20220606100342
**********************
确保完整转录的方法
此示例对原始方案稍有变化,但现在所有内容都记录到脚本中。 原始代码封装在一个脚本块中,并通过Out-Default
显式调用格式化程序。
PS> Get-Content scenario4.ps1
Start-Transcript scenario4.log -UseMinimalHeader
. {
Write-Host '1'
Write-Output '2'
Get-Location
Write-Host '4'
Write-Output '5'
} | Out-Default
Stop-Transcript
PS> ./scenario4.ps1
Transcript started, output file is scenario4.log
1
2
4
Path
----
/Users/user1/src/projects/transcript
5
Transcript stopped, output file is /Users/user1/src/projects/transcript/scenario4.log
请注意,最后 Write-Host
的调用仍然是无序的,因为 Write-Host
的优化未被传递到输出流中。
PS> Get-Content scenario4.log
**********************
PowerShell transcript start
Start time: 20220606101038
**********************
Transcript started, output file is s5
1
2
4
Path
----
/Users/user1/src/projects/transcript
5
**********************
PowerShell transcript end
End time: 20220606101038
**********************