模拟Shell执行.sh文件时getline()异常的问题排查与解决咨询
问题根源与修复方案
1. getline() 使用错误导致行数据异常
你代码里 getline(&line, &len, fp) 的用法完全错误:
line被声明为栈上的固定数组char line[MAX_CMD_BUFFER];,但getline的第一个参数要求是指向堆分配内存的指针(初始可设为NULL)。当你传入栈数组的地址时,getline会尝试调用realloc扩容栈内存,这属于未定义行为,直接导致行数据无法正确存储。
2. 两种简便修复方案
方案一:改用 fgets 替代 getline(更简单,无需动态内存管理)
fgets 可以直接读取数据到栈数组里,适配你的现有代码结构,修改后循环部分如下:
char line[MAX_CMD_BUFFER]; // 替换原有的getline循环 while (fgets(line, MAX_CMD_BUFFER, fp) != NULL) { // 先去掉换行符(fgets会把换行符读进来) size_t line_len = strlen(line); if (line_len > 0 && line[line_len-1] == '\n') { line[line_len-1] = '\0'; } // 后续的命令解析逻辑... }
方案二:正确使用 getline(需要动态内存管理)
如果坚持用 getline,要把 line 初始化为 NULL,让 getline 自动分配内存:
char *line = NULL; size_t len = 0; ssize_t read; while ((read = getline(&line, &len, fp)) != -1) { // 去掉换行符 if (read > 0 && line[read-1] == '\n') { line[read-1] = '\0'; } // 后续的命令解析逻辑... } free(line); // 循环结束后必须释放内存
3. 代码里的其他致命问题(必须一起修复)
prev_f 初始化问题:你用 malloc 分配了 prev_f,但没初始化,里面是随机值,
if(*prev_f == NULL)完全无效,应该改成:char* prev_f = malloc(MAX_CMD_BUFFER); memset(prev_f, 0, MAX_CMD_BUFFER); // 初始化为空字符串判断是否有历史命令应该用
if(strlen(prev_f) == 0)而不是*prev_f == NULL。c_bang 越界问题:
c_bang是char[3],你给c_bang[3] = '\0'会越界,应该改成c_bang[2] = '\0'。命令解析的鲁棒性问题:直接取前4个字符判断
echo,但如果命令前有空格(比如echo hello)就会判断错误,应该先跳过前导空白字符再解析命令名。exit 参数提取错误:固定从i=5到8取参数是硬编码,应该先找到命令后的第一个非空白字符,再提取参数,比如用 strtok 或者手动遍历。
修复后的示例代码片段(fgets版本)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAX_CMD_BUFFER 1024 void run_file(char* file){ const char* echo = "echo"; const char* ex = "exit"; const char* bang = "!!"; char* prev_f = malloc(MAX_CMD_BUFFER); memset(prev_f, 0, MAX_CMD_BUFFER); // 初始化空字符串 FILE *fp = fopen(file, "r"); if (fp == NULL) exit(EXIT_FAILURE); char line[MAX_CMD_BUFFER]; while (fgets(line, MAX_CMD_BUFFER, fp) != NULL) { // 去掉换行符 size_t line_len = strlen(line); if (line_len > 0 && line[line_len-1] == '\n') { line[line_len-1] = '\0'; } // 跳过空行 if (strlen(line) == 0) continue; // 跳过前导空白,找到命令起始位置 char *cmd_start = line; while (isspace((unsigned char)*cmd_start)) cmd_start++; if (*cmd_start == '\0') continue; // 全是空白的行直接跳过 // 判断是否是!!命令 if (strncmp(cmd_start, bang, 2) == 0 && (cmd_start[2] == '\0' || isspace((unsigned char)cmd_start[2]))) { if (strlen(prev_f) == 0) { printf("No previous command\n"); continue; } // 重新执行历史命令,复制到line后重新解析 strcpy(line, prev_f); cmd_start = line; while (isspace((unsigned char)*cmd_start)) cmd_start++; } // 判断echo命令 if (strncmp(cmd_start, echo, 4) == 0 && (cmd_start[4] == '\0' || isspace((unsigned char)cmd_start[4]))) { // 提取echo后的内容 char *echo_content = cmd_start + 4; while (isspace((unsigned char)*echo_content)) echo_content++; if (*echo_content != '\0') { printf("%s\n", echo_content); } strcpy(prev_f, line); continue; } // 判断exit命令 if (strncmp(cmd_start, ex, 4) == 0 && (cmd_start[4] == '\0' || isspace((unsigned char)cmd_start[4]))) { // 提取exit参数 char *exit_val_str = cmd_start + 4; while (isspace((unsigned char)*exit_val_str)) exit_val_str++; int v = 0; if (*exit_val_str != '\0') { v = atoi(exit_val_str); if (v < 0) v = 0; if (v > 255) v = 255; } printf("bye!\n"); strcpy(prev_f, line); fclose(fp); free(prev_f); exit(v); } // 无效命令处理 printf("invalid command: %s\n", line); strcpy(prev_f, line); } fclose(fp); free(prev_f); }
内容的提问来源于stack exchange,提问作者shinny.dogma
相关产品推荐
相关产品推荐

