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

popen执行系统命令返回冗余数据问题排查及代码优化求助

问题排查:execute_cmd返回垃圾字节的原因及优化方案

你遇到的execute_cmd返回垃圾内容的问题,主要是几个关键的内存操作和函数使用错误导致的,我来逐一拆解并给出修复方案:

核心问题分析

  1. path数组未初始化且无字符串终止符
    你定义了char path[100];但没有初始化,每次fread读取数据后,如果读取的字节数小于100,path数组剩余的位置会残留栈上的垃圾数据。而strcat依赖\0来判断字符串结束,未终止的path会把这些垃圾拼接到buffer里。

  2. fread参数使用错误
    fread(path, 100, 1, fp)的调用逻辑是“读取1个大小为100字节的块”,返回值是成功读取的块数(0或1),但你把它当成了读取的字节数。这会导致即使读取了部分数据,也无法正确判断实际读取的长度,进而无法给path添加终止符。

  3. execute_cmd未返回buffer指针
    函数声明是char *execute_cmd(char *cmd),但函数末尾没有return buffer;,这意味着调用avail_space = execute_cmd(buffer);时,拿到的是一个随机的垃圾指针,后续操作自然会出现异常数据。

  4. 固定大小的buffer存在溢出风险
    你给buffer分配了1024字节的固定空间,如果命令输出超过这个长度,会导致缓冲区溢出,破坏内存中的其他数据,表现为垃圾内容甚至程序崩溃。

优化后的代码方案

针对以上问题,我重新编写了execute_cmd函数,同时修复了主函数中的一些细节问题:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *execute_cmd(char *cmd) {
    FILE *fp;
    // 每次读取的字节数,留1位给终止符
    size_t chunk_size = 128;
    // 初始缓冲区大小
    size_t buffer_size = 1024;
    char *buffer = malloc(buffer_size);
    if (!buffer) {
        perror("Failed to allocate buffer");
        exit(EXIT_FAILURE);
    }
    // 初始化缓冲区为空字符串
    buffer[0] = '\0';

    fp = popen(cmd, "r");
    if (fp == NULL) {
        perror("Failed to run command");
        free(buffer);
        exit(EXIT_FAILURE);
    }
    printf("Running command is: %s\n", cmd);

    char *chunk = malloc(chunk_size);
    if (!chunk) {
        perror("Failed to allocate chunk");
        free(buffer);
        pclose(fp);
        exit(EXIT_FAILURE);
    }

    size_t bytes_read;
    while ((bytes_read = fread(chunk, 1, chunk_size - 1, fp)) > 0) {
        // 添加字符串终止符,确保chunk是合法字符串
        chunk[bytes_read] = '\0';
        // 检查缓冲区是否足够,不够则扩容
        size_t current_len = strlen(buffer);
        if (current_len + bytes_read + 1 > buffer_size) {
            buffer_size *= 2;
            char *temp = realloc(buffer, buffer_size);
            if (!temp) {
                perror("Failed to reallocate buffer");
                free(chunk);
                free(buffer);
                pclose(fp);
                exit(EXIT_FAILURE);
            }
            buffer = temp;
        }
        // 拼接内容
        strcat(buffer, chunk);
    }

    free(chunk);
    pclose(fp);
    // 返回动态分配的缓冲区,调用者需要负责free
    return buffer;
}

int main() {
    // 注意转义引号,或者用单引号包裹awk的参数
    char *cmd = "df -h | grep \"/mnt\" | awk '{print $4}'";
    char *avail_space = execute_cmd(cmd);
    
    if (avail_space) {
        // 去除可能的换行符(df输出会带换行)
        size_t len = strlen(avail_space);
        if (len > 0 && avail_space[len - 1] == '\n') {
            avail_space[len - 1] = '\0';
            len--;
        }

        printf("Available space is: %s\n", avail_space);
        if (len > 0) {
            char units = avail_space[len - 1];
            // 提取数字部分
            char *endptr;
            float number = strtof(avail_space, &endptr);
            if (endptr != avail_space) {
                printf("Number is: %.2f, Units: %c\n", number, units);
            } else {
                printf("Failed to parse number from output\n");
            }
        }
        // 记得释放动态分配的内存
        free(avail_space);
    }

    return 0;
}

优化点说明

  • 动态扩容缓冲区:使用realloc根据实际输出大小动态调整buffer的大小,避免溢出。
  • 正确使用fread:改为fread(chunk, 1, chunk_size - 1, fp),返回实际读取的字节数,同时给chunk添加\0终止符,确保是合法字符串。
  • 返回正确的指针:execute_cmd函数末尾返回buffer,调用者可以正确获取命令输出。
  • 内存管理:添加了内存分配失败的错误处理,并且在主函数中记得释放avail_space,避免内存泄漏。
  • 处理换行符:df的输出会带有换行符,优化后的代码会去除换行符,避免后续解析出错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:53:56