You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在后台运行的.NET Core应用执行Bash命令时出现挂起问题

后台运行.NET应用时调用Bash命令挂起的问题解决

我们的应用是部署在Linux主机(基于定制Yocto Linux嵌入式镜像)上的服务,通过调用Bash命令修改系统配置(如日期、systemd网络配置等),实现了RunCommand方法。直接运行./Controller一切正常,但后台运行./Controller &时应用会永久挂起。

原实现代码:

public static Result<string> RunCommand(string command, bool wait = true)
{
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return new Result<string>() { ResultObject = "done" };

     Result<string> result = new Result<string>();
     using (Process proc = new())
     {
         proc.StartInfo.FileName = "/bin/bash";
         proc.StartInfo.Arguments = "-c \" " + command + " \"";
         proc.StartInfo.UseShellExecute = true;
         proc.StartInfo.RedirectStandardOutput = true;
         proc.StartInfo.RedirectStandardError = true;
         proc.StartInfo.CreateNoWindow = true;
        
         proc.Start();
     
         proc.OutputDataReceived += Proc_OutputDataReceived;

         result.ResultObject = proc.StandardOutput.ReadToEnd();
         var error = proc.StandardError.ReadToEnd();
         if (!string.IsNullOrWhiteSpace(error))
         {
             result.HasError = true;
             result.ErrorMessage = error;
             result.ResultCode = Enums.ResultCode.UnspecifiedError;
         }
         if (wait)
             proc.WaitForExit();
         proc.OutputDataReceived -= Proc_OutputDataReceived;
     }
     return result;
}

问题根源

  1. UseShellExecute与重定向冲突
    当设置UseShellExecute = true时,.NET会通过系统shell启动进程,但此时不支持重定向StandardOutput/StandardError。后台运行时,子进程的输出缓冲区无法正常被读取,满了之后会阻塞子进程,进而导致主进程挂起。
  2. 同步读取输出的死锁风险
    先调用StandardOutput.ReadToEnd()再读StandardError.ReadToEnd(),如果子进程往stderr写入大量内容,stderr缓冲区满了之后子进程会阻塞,而主进程还在等待stdout读完,形成死锁。
  3. OutputDataReceived事件未生效
    注册了事件但未调用proc.BeginOutputReadLine(),事件监听根本没启动,完全没起到作用。

修复后的代码

public static Result<string> RunCommand(string command, bool wait = true)
{
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 
        return new Result<string>() { ResultObject = "done" };

    Result<string> result = new Result<string>();
    using (Process proc = new Process())
    {
        // 核心:禁用UseShellExecute,才能正确重定向输出
        proc.StartInfo.FileName = "/bin/bash";
        // 优化参数拼接,避免命令含空格时的引号问题
        proc.StartInfo.Arguments = $"-c \"{command}\"";
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardError = true;
        proc.StartInfo.CreateNoWindow = true;

        // 用异步方式读取输出,彻底避免死锁
        StringBuilder outputBuilder = new StringBuilder();
        StringBuilder errorBuilder = new StringBuilder();
        proc.OutputDataReceived += (sender, e) => 
        {
            if (!string.IsNullOrEmpty(e.Data))
                outputBuilder.AppendLine(e.Data);
        };
        proc.ErrorDataReceived += (sender, e) => 
        {
            if (!string.IsNullOrEmpty(e.Data))
                errorBuilder.AppendLine(e.Data);
        };

        proc.Start();

        // 启动异步读取流程
        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();

        if (wait)
        {
            // 添加超时处理,防止命令执行卡住导致主进程挂起
            if (!proc.WaitForExit(30000))
            {
                proc.Kill();
                result.HasError = true;
                result.ErrorMessage = "命令执行超时";
                result.ResultCode = Enums.ResultCode.TimeoutError;
                return result;
            }
        }

        result.ResultObject = outputBuilder.ToString().Trim();
        string errorOutput = errorBuilder.ToString().Trim();
        if (!string.IsNullOrWhiteSpace(errorOutput))
        {
            result.HasError = true;
            result.ErrorMessage = errorOutput;
            result.ResultCode = Enums.ResultCode.UnspecifiedError;
        }
    }
    return result;
}

关键修复说明

  • 禁用UseShellExecute:设置为false是.NET中重定向进程输出的必要前提,确保主进程能直接控制子进程的输出流。
  • 异步读取输出:通过BeginOutputReadLine和BeginErrorReadLine配合事件监听,避免同步读取顺序导致的死锁。
  • 参数拼接优化:用字符串插值处理bash命令参数,避免多余空格和引号解析错误。
  • 超时控制:添加进程等待超时逻辑,防止命令执行异常时主进程无限挂起。

内容的提问来源于stack exchange,提问作者Marcomattia Mocellin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:55:59