【发布时间】:2021-12-07 16:22:52
【问题描述】:
注意:我正在尝试将
packer.exe作为后台进程运行,以解决azure-arm构建器的特定问题,我需要观察输出。我没有使用Start-Process,因为我不想使用中间文件来消耗输出。
我有以下代码设置packer.exe 在后台运行,这样我就可以使用它的输出并根据某个日志消息采取行动。这是一个较大脚本的一部分,但这是有问题的部分,行为不正确:
$builderDir = ( Split-Path -Parent $PSCommandPath )
Push-Location $builderDir
# Set up the packer command to run asynchronously
$pStartProps = @{
FileName = ( Get-Command -CommandType Application packer ).Source
Arguments = "build -var-file ""${builderDir}\win-dev.pkrvars.hcl"" -only ""azure-arm.base"" ."
UseShellExecute = $false
RedirectStandardOutput = $true
RedirectStandardError = $false
LoadUserProfile = $true
}
$pStartInfo = New-Object ProcessStartInfo -Property $pStartProps
$p = [Process]::Start($pStartInfo)
while ( $null -ne ( $output = $p.StandardOutput.Readline() ) -or !$p.HasExited ) {
# Do stuff
}
基本上,while 条件的( $output = $p.StandardOutput.Readline() ) 部分似乎一直挂起,直到有更多输出可供读取。我不确定这是为什么,因为如果没有更多的输出,StreamReader.Readline()should either return the next line to be read, or null。关于我期望获得的日志消息,我对此进行了相当多的处理,因此当没有进一步的输出消耗时读取 STDOUT 时阻塞会使脚本无用。在packer.exe 继续执行时,它还在前台执行其他操作。
我可以在调试器中确认Readline() 确实读取了空行("" 的值),这似乎发生在没有进一步的输出消耗时。这可能是相切的,但这也会导致调试器出错。
发生此问题时,VSCode 调试器会在此位置上显示$output = $p.StandardOutput.Readline() 突出显示几秒钟,然后调试器停止(一切都消失,不再跟踪变量等),直到 Readline() 停止阻塞和执行继续,此时调试器似乎重新初始化了跟踪的变量、监视的表达式等。所以当这种情况发生时我根本无法使用调试器。甚至 PowerShell Integrated Console(与调试器一起使用的那个)也挂起,我无法输入任何内容。
对于完整的上下文,这个脚本的目标是让packer.exe 在我不断循环到:
- 显示来自
packer.exe的更多输出 - 检查是否存在特定日志消息
- 给
packer.exe一点时间尝试自己做它需要做的事情 - 如果等待时间过长,我会针对节点执行一个脚本,因为
packer.exe应该自己完成的操作可能会失败- 我正在使用
Invoke-AzVMRunCommand来执行此操作,对于我正在解决的问题,在packer.exe状态下无法执行此操作。它必须在packer.exe运行本身的带外执行。
- 我正在使用
- 在应用解决方法后继续构建,我只是继续将
packer.exe的输出转发到控制台,直到进程退出
但是由于脚本在没有输出时挂起,所以第 4 步将永远无法工作,因为我必须给加壳程序时间来尝试自己完成配置,这就是我在第一名。
为什么Readline() 在这里阻塞?我做错了什么吗?无论我在 Windows PowerShell 还是 PowerShell Core 中运行我的脚本,都会出现此行为。
【问题讨论】:
-
哈哈,很高兴知道。这个脚本可能最终会在 Azure 中的 *nix 构建器上运行,所以我会记住在
StreamReader周围可能存在跨平台的奇怪之处,尽管从文件中读取是我试图避免的事情,所以它可能与我无关。不过,目前的目标是从工作站上获得这座建筑。这个打包机的东西让我把头发扯掉了! -
我认为 file 的使用是我的示例代码附带的——我只是为了方便而使用它。如果您将示例调整为您的
Process场景(而不是while ($true),使用while (-not $p.HasExited)等等 - 尽管您可能希望确保在退出循环。 -
是的,我不得不求助于布尔标志来结束输入,因为我依赖
null,这意味着没有新信息可供阅读。但是,如果null表示输出流已关闭,我应该能够假设该进程也已退出(尽管最后我仍然检查这个)。无论哪种情况,我都在尝试您的建议,但可能要到明天早上才能测试。 -
也许只是检查
$task.IsCompleted之后循环会找到最后一行,如果有的话。 -
我实现它的方式不需要它。所有这些都在循环的顶部完成,大部分代码都是读取该行后要做什么的逻辑。无论如何它都很好用,所以谢谢。
标签: .net powershell .net-core powershell-core