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

C#调用带Awk管道的CMD命令时进程无法退出如何解决

C# 调用Cmd执行带管道命令时进程卡死无法退出修复

问题相关代码

public class CmdHelper
{
    public static string StartCmd(string commandLine)
    {
        commandLine = commandLine.Trim().TrimStart('&') + "&exit";
        string outputMsg = "";
        Process pro = new Process();
        pro.StartInfo.FileName = "cmd.exe";
        pro.StartInfo.UseShellExecute = false;
        pro.StartInfo.RedirectStandardError = true;
        pro.StartInfo.RedirectStandardInput = true;
        pro.StartInfo.RedirectStandardOutput = true;
        pro.StartInfo.CreateNoWindow = false;
        pro.Start();
        pro.StandardInput.WriteLine(commandLine);
        pro.StandardInput.AutoFlush = true;
        pro.StartInfo.StandardErrorEncoding = Encoding.UTF8;
        pro.StartInfo.StandardOutputEncoding = Encoding.UTF8;
        outputMsg += pro.StandardOutput.ReadToEnd();

        pro.WaitForExit();
        pro.Close();

        return outputMsg;
    }

    public static void CommitBundleToSvn()
    {
        string folderPath = EditorConst.BundlesPath + VEngine.Editor.Builds.Settings.GetPlatformName();
        string command = "svn status " + folderPath;
        string regaxPattern = ".*&exit";
        string output = "";
        string[] regexRes = {};
        command = "svn st " + folderPath+ " | awk \"{if ($1 == \\\"?\\\") {print $2} }\""; 
        output = CmdHelper.StartCmd(command);
        UnityEngine.Debug.LogError(command);
        UnityEngine.Debug.LogError(output);
    }
}

执行的命令如下:

svn st <目标文件夹路径> | awk "{if ($1 == \"?\") {print $2} }"

现象说明

  • 上述命令直接在Windows Cmd窗口手动执行时,可以正常获取返回结果
  • 代码中调用CommitBundleToSvn()方法时,Cmd进程持续挂起无法正常退出
  • 将执行命令替换为不带管道的svn status <目标文件夹路径>时,程序运行表现符合预期

问题根因

代码存在三处隐患,带管道的命令执行时会触发问题:

  1. 编码设置时机错误:StandardErrorEncoding、StandardOutputEncoding属性必须在调用Process.Start()前赋值,进程启动后修改这两个属性不会生效,会导致流读取编码异常、阻塞。
  2. 标准流死锁:同时重定向标准输出、标准错误流时,同步调用ReadToEnd()读取其中一个流,如果另一个流的缓冲区被子进程写满(默认缓冲区约4KB),子进程会阻塞在写流操作上,主进程则阻塞在等待子进程退出/读流完成,形成死锁。不带管道的svn status输出量小,没有触发缓冲区写满,所以碰巧运行正常。
  3. 输入流未关闭:向标准输入写入命令后没有关闭输入流,管道右侧的awk命令会持续等待后续输入,不会主动结束执行。

修复方案

调整StartCmd方法逻辑,修正配置顺序、关闭输入流、用异步读取避免死锁,修复后代码如下:

public static string StartCmd(string commandLine)
{
    commandLine = commandLine.Trim().TrimStart('&') + "&exit";
    string outputMsg = "";
    Process pro = new Process();
    pro.StartInfo.FileName = "cmd.exe";
    pro.StartInfo.UseShellExecute = false;
    pro.StartInfo.RedirectStandardError = true;
    pro.StartInfo.RedirectStandardInput = true;
    pro.StartInfo.RedirectStandardOutput = true;
    pro.StartInfo.CreateNoWindow = false;
    // 所有StartInfo配置必须在Start调用前完成
    pro.StartInfo.StandardErrorEncoding = Encoding.UTF8;
    pro.StartInfo.StandardOutputEncoding = Encoding.UTF8;
    
    pro.Start();
    
    // 写入命令后关闭标准输入,通知子进程输入已结束
    pro.StandardInput.WriteLine(commandLine);
    pro.StandardInput.AutoFlush = true;
    pro.StandardInput.Close();
    
    // 异步同时读取两个流,避免死锁
    Task<string> readOutputTask = pro.StandardOutput.ReadToEndAsync();
    Task<string> readErrorTask = pro.StandardError.ReadToEndAsync();
    
    pro.WaitForExit();
    
    outputMsg = readOutputTask.Result;
    string errorMsg = readErrorTask.Result;
    if (!string.IsNullOrEmpty(errorMsg))
    {
        UnityEngine.Debug.LogError($"命令执行错误输出: {errorMsg}");
    }
    
    pro.Close();
    return outputMsg;
}

如果不需要捕获错误输出,也可以直接将pro.StartInfo.RedirectStandardError设为false,不重定向错误流,也可以规避流死锁问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:39:26