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

流读取exec执行的C程序与Shell脚本输出的行为异常问题

问题根因

这是标准C库的默认IO缓冲策略导致的正常行为,不属于任何组件的bug:

  • 当C程序的stdout指向交互式终端时,printf默认采用行缓冲策略,每次输出换行符就会自动刷新缓冲区到输出流
  • 当通过Go的exec.Command调用子进程时,子进程的stdout/stderr默认关联的是管道而非终端,此时C标准库会自动切换为全缓冲策略,缓冲区默认大小通常为4KB,只有缓冲区填满、程序主动调用刷新接口、或者进程退出时才会将缓冲区内容写入管道,所以会看到10秒后程序退出才一次性输出所有内容
  • Bash的echo命令默认每次执行都会主动刷新输出缓冲区,所以即使输出到管道也能实现每秒一行的流式输出
解决方案

方案1:修改C程序(最可靠)

可以任选以下两种修改方式之一:

  1. 每次printf后手动刷新缓冲区:
#include <stdio.h>
#include <unistd.h>

int main() {
    int i;
    for (i=1; i<=10; i++) {
        printf("run %d\n", i);
        fflush(stdout); // 新增行,主动刷新stdout缓冲区
        sleep(1);
    }
    return 0;
}
  1. 程序初始化时直接将stdout设置为行缓冲模式:
#include <stdio.h>
#include <unistd.h>

int main() {
    // 程序开头设置stdout为行缓冲,第二个参数为NULL表示由标准库分配缓冲区,第三个参数_IOLBF表示行缓冲,第四个参数为缓冲区大小,传0使用默认大小
    setvbuf(stdout, NULL, _IOLBF, 0);
    int i;
    for (i=1; i<=10; i++) {
        printf("run %d\n", i);
        sleep(1);
    }
    return 0;
}

方案2:无需修改C程序,在调用侧使用stdbuf工具强制行缓冲

Linux系统自带的stdbuf工具可以修改指定程序的标准IO缓冲策略,不需要修改源码,Go代码调整如下:

// 将原来的cmd2定义替换为这一行
cmd2 := exec.Command("stdbuf", "-oL", "./testcommands/testcommand")

其中-oL参数表示将标准输出设置为行缓冲模式。

额外注意事项

你当前Go代码中使用io.MultiReader(stdout, stderr)的实现存在逻辑缺陷:MultiReader会先完全读取第一个输入流(stdout)的所有内容,才会开始读取第二个输入流(stderr),如果子进程有输出到stderr的内容,会被阻塞到stdout完全关闭后才会被读取,不符合实时流式输出的预期。建议改为启动两个goroutine分别读取stdout和stderr。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 00:06:02