c# - 启动进程的稳健方法,读取所有输出并等待超时
问题描述
.NET Core 具有Process
可以启动进程的类。您还可以读取它的 stdout 和 stderr 流并向 stdin 写入一些内容。您可以等待进程在指定时间退出。问题是,它需要大量代码,并且在所有情况下仍然无法正常工作。
同步读取不会错过任何一行输出,并准确告诉您何时拥有所有输出。但是如果被调用的程序产生太多输出(> 4 kB?),它可能会阻塞。
异步读取应该解决“缓冲区已满”阻塞,但无法告诉您何时拥有所有输出。它可能会在一开始就错过一些输出。
这个问题及其最高投票答案的所有评论很好地总结了各种问题。
所以我正在寻找一种可以:
- 使用参数启动进程
- 让我将数据发送到它的输入
- 获取其输出的全部内容(stdout 和 stderr)
- 等待指定的时间,如果没有及时返回则终止进程
- 当我得到最终结果时让我继续前进(超时/退出代码和到目前为止的所有输出)
适用于 Windows 和 Linux 上的 .NET Core 3.1 的 2020 版。
如果 Microsoft 的某个人可以将解决方案添加到他们的稀疏文档中,将不胜感激。我再也找不到相关的 GitHub 存储库来报告文档不足。
这是我现在拥有的,它不适用于更大的输出:(进程不会退出,需要被杀死)
var psi = new ProcessStartInfo
{
FileName = "dotnet",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
psi.ArgumentList.Add("--list-runtimes"); // A few pages output on my PC
string standardOutput;
string standardError;
using (var process = Process.Start(psi))
{
bool timedOut = false;
// Sync version
if (!process.WaitForExit(10000))
{
try
{
// Try to clean things up
process.Kill();
}
catch
{
// Might have exited after all during the short period of time before
// calling Kill(). And if it fails for other reasons, we can't help it here.
}
timedOut = true;
}
// BEGIN Async alternative
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
try
{
await process.WaitForExitAsync(cts.Token);
}
catch (OperationCanceledException ex) when (ex.CancellationToken == cts.Token)
{
try
{
// Try to clean things up
process.Kill();
}
catch
{
// Might have exited after all during the short period of time before
// calling Kill(). And if it fails for other reasons, we can't help it here.
}
timedOut = true;
}
// END Async alternative
standardOutput = process.StandardOutput.ReadToEnd();
standardError = process.StandardError.ReadToEnd();
if (timedOut)
{
logger?.LogError($"The command did not complete in time.\n" +
$"Output: {standardOutput.TrimEnd()}\nError: {standardError.TrimEnd()}");
standardOutput = null;
}
else if (process.ExitCode != 0)
{
logger?.LogError($"The command failed with exit code {process.ExitCode}.\n" +
$"Output: {standardOutput.TrimEnd()}\nError: {standardError.TrimEnd()}");
standardOutput = null;
}
}
解决方案
推荐阅读
- sql - 将整数的 SUM 转换为 HH:MM 格式
- java - 将普通的旧“for循环”转换为Java 8“forEach”
- java - 经纪人确认 Micronaut 2.2.1 中的 Rabbit MQ 没有工作
- c# - 单个元素上的订单集合
- sqlalchemy - SQLAlchemy 的 column_property 中的条件
- kubernetes - Kubernetes Helm 图表,如果条件在数组中
- android - Android - 在“network_security_config.xml”中以纯文本形式存储证书哈希是否足够安全?
- php - 搜索框 (AJAX) 不会加载请求的数据
- kubernetes - 从 Azure DevOps 部署到 AKS 时如何传入环境变量
- c# - 命名空间名称身份验证在命名空间 Microsoft.AspNetCore.Components.WebAssembly.Authentication 中不存在