.net - 没有输出时 Process.StandardOutput.Readline() 挂起
问题描述
注意:我正在尝试
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
}
基本上,条件的( $output = $p.StandardOutput.Readline() )
一部分while
似乎一直挂起,直到有更多的输出要读取。我不确定为什么会这样,因为StreamReader.Readline()
应该返回要读取的下一行,或者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 中运行我的脚本,都会出现此行为。
解决方案
StreamReader.ReadLine()
被设计阻止。有一个异步替代方法,
.ReadLineAsync()
它返回一个Task<string>
实例,您可以通过其属性轮询完成,.IsCompleted
而不会阻塞您的前台线程(轮询是 PowerShell 中的唯一选项,因为它没有类似于 C# 的语言功能await
)。
这是一个简化的示例,侧重于从StreamReader
恰好是文件的实例中异步读取,新行仅定期添加到该实例中;用于Ctrl-C中止。
如果您将其调整为您的标准输出读取代码,我希望代码能够正常工作System.Diagnostics.Process
。
# Create a sample input file.
$n=3
1..$n > tmp.txt
# Open the file for reading, and convert it to a System.IO.StreamReader instance.
[IO.StreamReader] $reader =
[IO.File]::Open("$pwd/tmp.txt", 'Open', 'Read', 'ReadWrite')
try {
$task = $reader.ReadLineAsync() # Start waiting for the first line.
while ($true) { # Loop indefinitely to wait for new lines.
if ($task.IsCompleted) { # A new line has been received.
$task.Result # Output
# Start waiting for the next line.
$task.Dispose(); $task = $reader.ReadLineAsync();
}
else { # No new line available yet, do other things.
Write-Host '.' -NoNewline
Start-Sleep 1
}
# Append a new line to the sample file every once in a while.
if (++$n % 10 -eq 0) { $n >> tmp.txt }
}
}
finally {
$reader.Dispose()
}
推荐阅读
- c# - 开发团队中带有服务总线触发器的 Azure Functions V2
- javascript - 如何在 React JS 中为文本设置颜色
- html - CSS:没有 100% 高度/宽度的滚动条
- google-cloud-platform - GCP是否为vm实例分配子域名?
- maven - Sonarqube [http://loalhost:9000/batch/index] 无效:[404] 错误
- c# - 动画后世界位置变化 - Unity
- r - 将数据框中的值分配给R中的矩阵的最快方法?
- sql - 将 BULK COLLECT 与 rownum 一起使用
- javascript - 带有折叠的动态引导列表无法正常工作
- python - 比较来自两个不同数据帧的列元素,其中条件基于 pandas 中的函数