首页 > 解决方案 > 启动进程的稳健方法,读取所有输出并等待超时

问题描述

.NET Core 具有Process可以启动进程的类。您还可以读取它的 stdout 和 stderr 流并向 stdin 写入一些内容。您可以等待进程在指定时间退出。问题是,它需要大量代码,并且在所有情况下仍然无法正常工作。

同步读取不会错过任何一行输出,并准确告诉您何时拥有所有输出。但是如果被调用的程序产生太多输出(> 4 kB?),它可能会阻塞。

异步读取应该解决“缓冲区已满”阻塞,但无法告诉您何时拥有所有输出。它可能会在一开始就错过一些输出。

这个问题及其最高投票答案的所有评论很好地总结了各种问题。

所以我正在寻找一种可以:

适用于 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;
    }
}

标签: c#linuxwindows.net-coreprocess

解决方案


推荐阅读