popen执行系统命令返回冗余数据问题排查及代码优化求助
问题排查:execute_cmd返回垃圾字节的原因及优化方案
你遇到的execute_cmd返回垃圾内容的问题,主要是几个关键的内存操作和函数使用错误导致的,我来逐一拆解并给出修复方案:
核心问题分析
path数组未初始化且无字符串终止符
你定义了char path[100];但没有初始化,每次fread读取数据后,如果读取的字节数小于100,path数组剩余的位置会残留栈上的垃圾数据。而strcat依赖\0来判断字符串结束,未终止的path会把这些垃圾拼接到buffer里。fread参数使用错误fread(path, 100, 1, fp)的调用逻辑是“读取1个大小为100字节的块”,返回值是成功读取的块数(0或1),但你把它当成了读取的字节数。这会导致即使读取了部分数据,也无法正确判断实际读取的长度,进而无法给path添加终止符。execute_cmd未返回buffer指针
函数声明是char *execute_cmd(char *cmd),但函数末尾没有return buffer;,这意味着调用avail_space = execute_cmd(buffer);时,拿到的是一个随机的垃圾指针,后续操作自然会出现异常数据。固定大小的
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
相关产品推荐
相关产品推荐

