Linux下C语言编写简易Shell:实现输入缓冲区有内容时按Ctrl+D(EOF)立即退出的方法
解决Shell中Ctrl+D(EOF)的处理问题
我明白你遇到的问题了——当输入命令(比如ls)后直接按Ctrl+D,你的Shell会执行命令而不是立即退出,只有空行按Ctrl+D才会触发退出。这是因为你对fgets和feof的工作逻辑理解有点偏差,咱们一步步理清楚:
为什么当前代码的feof不生效?
Ctrl+D在Linux中的作用是将当前输入缓冲区的内容立即发送给程序,而不是直接触发EOF:
- 当你输入
ls后按Ctrl+D,输入缓冲区里的ls会被发送给程序,fgets成功读取到这段内容(此时stdin并没有到达EOF,因为缓冲区刚被清空,但还可以继续输入),所以feof(stdin)返回false,程序会继续执行后续的命令逻辑。 - 只有当输入缓冲区为空时按下Ctrl+D,才会真正触发stdin的EOF状态,此时
fgets返回NULL,feof(stdin)才会返回true。
解决方案:检测EOF导致的不完整行读取
要实现“输入命令后按Ctrl+D就退出”,我们需要在两个关键点检测EOF:
- 当
fgets直接返回NULL时(空行按Ctrl+D),直接退出。 - 当
fgets读取到的内容没有换行符,且是因为EOF导致的(而不是缓冲区太小),立即退出,不执行命令。
修改后的代码片段
下面是调整后的核心逻辑部分,替换你原来的输入读取和扩容代码:
int main(int argc, char** argv) { size_t buff_capacity = 48 * sizeof(char); char* buff = malloc(buff_capacity); if (buff == NULL) { perror("kell: malloc"); return EXIT_FAILURE; } while (1) { printf("kell$ "); // 1. 先检测fgets是否直接返回NULL(空行Ctrl+D) if (fgets(buff, buff_capacity, stdin) == NULL) { printf("\n"); free(buff); // 记得释放内存 exit(EXIT_SUCCESS); } size_t len = strlen(buff); // 检查是否读取到不完整的行(没有换行符) if (len > 0 && buff[len - 1] != '\n') { // 2. 检测是否是EOF导致的不完整读取 if (feof(stdin)) { printf("\n"); free(buff); exit(EXIT_SUCCESS); } // 缓冲区已满,执行扩容逻辑 while (1) { char* tmp = resize(buff, buff_capacity * 2); if (tmp == NULL) { fprintf(stderr, "kell: not enough memory\n"); break; } buff = tmp; buff_capacity *= 2; // 继续读取剩余内容,同时检测EOF if (fgets(buff + len, buff_capacity - len, stdin) == NULL) { printf("\n"); free(buff); exit(EXIT_SUCCESS); } len = strlen(buff); // 读取到换行符,结束扩容 if (buff[len - 1] == '\n') { break; } // 再次检测是否是EOF导致的不完整读取 if (feof(stdin)) { printf("\n"); free(buff); exit(EXIT_SUCCESS); } } } // 后面的分词与执行逻辑保持不变... buff[strlen(buff) - 1] = '\0'; char* argsToSendExec[MAX_ARGC]; int a = 0; char* token = strtok(buff, " "); while (token != NULL) { argsToSendExec[a++] = token; token = strtok(NULL, " "); } argsToSendExec[a] = NULL; pid_t cpid = fork(); if (cpid < 0) { perror("fork"); free(buff); return EXIT_FAILURE; } if (cpid == 0) { if (execvp(argsToSendExec[0], argsToSendExec) == -1) { perror("execvp"); return EXIT_FAILURE; } } wait(NULL); } free(buff); return EXIT_SUCCESS; }
关键修改点说明
- 检查fgets返回值:这是检测EOF最直接的方式,当空行按Ctrl+D时,
fgets会返回NULL,此时直接退出。 - 检测不完整行的EOF:当输入
ls后按Ctrl+D,fgets读取到ls但没有换行符,此时feof(stdin)会返回true(因为发送缓冲区内容后,stdin进入EOF状态),我们就可以触发退出逻辑,不执行后续的命令。 - 内存泄漏修复:添加了
free(buff)的逻辑,避免内存泄漏。
这样修改后,无论你是在空行按Ctrl+D,还是输入命令后直接按Ctrl+D,Shell都会立即退出,不会执行命令。
内容的提问来源于stack exchange,提问作者Billie Jean
相关产品推荐
相关产品推荐

