代码之家  ›  专栏  ›  技术社区  ›  ygoe

启动进程、读取所有输出并等待超时的健壮方法

  •  1
  • ygoe  · 技术社区  · 4 年前

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

    异步读取应该可以解决“缓冲区已满”的阻塞问题,但不能告诉您什么时候有所有的输出。它可能会在开始时错过一些输出。

    This question 投票率最高的所有评论都很好地概括了各种各样的问题。

    因此,我正在寻找一种能够:

    • 用参数启动进程
    • 让我把数据发送到它的输入
    • 等待指定的时间,如果进程没有及时返回,则终止进程
    • 当我得到最终结果(超时/退出代码和到目前为止的所有输出)时,让我继续

    Windows和Linux上的.NET Core 3.1的2020版。

    异步等待进程也很好。我有 some code 为此,还有一次,它的作用是巨大的。

    这是我现在所拥有的,但它对于更大的输出是不起作用的:(进程不会退出,需要终止)

    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;
        }
    }
    
    0 回复  |  直到 4 年前