C语言Shell命令程序execvp输出格式异常排查求助
Shell命令程序execvp输出格式问题修复
我用C语言开发了一款Shell命令程序,程序可正常运行,但execvp的输出格式存在问题。多次检查代码仍未定位到问题根源,尝试添加换行符后问题反而更严重。
问题现象
执行命令后,Shell提示符osh> 会出现在命令输出的中间位置(比如执行ls后,提示符跑到了输出行的开头,而非输出完成后换行显示)。
原代码
#include <stdio.h> #include <unistd.h> #include <string.h> #include <wait.h> #include <stdlib.h> #include <termios.h> #include <fcntl.h> #define MAX_LINE 80 /*Maximum length of a command*/ #define MAX_HST 10 int main(void){ char getInput[MAX_LINE]; char *args[MAX_LINE/2+1]; /*command line arguments*/ int should_run = 1; /*Flag to determine when to exit the program*/ int numCommand = 0; int cmdHst = 0; char *cmdHistory[MAX_HST]; char *myCmd; while (should_run = 1){ printf("osh> "); fflush(stdout); fgets(getInput, MAX_LINE, stdin);//read input command if(strcmp(getInput, "!!\n") == 0){//Command history if(cmdHst == 0){ printf("No previous commands.\n"); } for (int i= 0; i<cmdHst; i++){//display all commands in history strncpy(getInput, cmdHistory[i], MAX_LINE); printf("%s\n", getInput); } } if (cmdHst < MAX_HST){ cmdHistory[cmdHst] = strdup(getInput); cmdHst++; } else{//shift commands in history to fill gaps for(int i = 1; i < cmdHst; i++){ free(cmdHistory[i-1]); cmdHistory[i-1]= strdup(cmdHistory[i]); } free(cmdHistory[cmdHst-1]); cmdHistory[MAX_HST-1] = strdup(getInput); } //parsing commands into tokens numCommand = 0; args[numCommand] = strtok(getInput, " \n"); while(args[numCommand] != NULL){ numCommand++; args[numCommand] = strtok(NULL, " \n"); } args[numCommand]=NULL; if(strcmp(args[0], "exit")==0){//Check for exit command should_run =0; break; } pid_t pid = fork();//Create child process if(pid < 0){ fprintf(stderr, "Fork Failed\n"); return 1; } else if(pid == 0){ if(execvp(args[0], args)==-1){ fprintf(stderr, "Command Not Found.\n"); exit(1); } else{ if(args[numCommand-1][0] != '&'){ wait(NULL); } } } } for(int i = 0; i < cmdHst; i++){//free histrry array free(cmdHistory[i]); } return 0; }
问题根源与修复方案
父进程未等待前台子进程完成
- 原代码中父进程
fork后直接回到循环,会先打印osh>提示符,而子进程的输出后出现,导致格式混乱。且子进程中调用wait(NULL)完全错误,子进程不应等待自身。 - 修复:父进程判断是否为后台命令(带
&),前台命令则等待子进程结束,后台命令则不等待,同时要从参数中移除&避免传给execvp。
- 原代码中父进程
!!命令逻辑错误- 原代码会打印所有历史命令并覆盖
getInput,且仍会将!!加入历史,不符合!!重复上一个命令的预期。 - 修复:
!!仅加载上一个历史命令到getInput,无历史时提示后跳过后续执行,且将实际执行的命令加入历史。
- 原代码会打印所有历史命令并覆盖
循环条件错误
- 原
while (should_run = 1)是赋值操作,会导致循环永远无法终止(除了exit命令的break),应改为while (should_run)。
- 原
修正后的代码
#include <stdio.h> #include <unistd.h> #include <string.h> #include <wait.h> #include <stdlib.h> #include <termios.h> #include <fcntl.h> #define MAX_LINE 80 /*Maximum length of a command*/ #define MAX_HST 10 int main(void){ char getInput[MAX_LINE]; char *args[MAX_LINE/2+1]; /*command line arguments*/ int should_run = 1; /*Flag to determine when to exit the program*/ int numCommand = 0; int cmdHst = 0; char *cmdHistory[MAX_HST]; while (should_run) { printf("osh> "); fflush(stdout); // 读取输入,处理EOF if (fgets(getInput, MAX_LINE, stdin) == NULL) { should_run = 0; break; } // 处理空输入(仅回车) if (strcmp(getInput, "\n") == 0) { continue; } // 处理!!命令:重复上一个历史命令 if(strcmp(getInput, "!!\n") == 0){ if(cmdHst == 0){ printf("No previous commands.\n"); continue; } else { // 复制最后一个历史命令到输入缓冲区 strncpy(getInput, cmdHistory[cmdHst-1], MAX_LINE-1); getInput[MAX_LINE-1] = '\0'; printf("%s", getInput); // 打印要执行的命令 } } // 将命令加入历史 if (cmdHst < MAX_HST){ cmdHistory[cmdHst] = strdup(getInput); cmdHst++; } else{// 历史满时,移除最早的命令,添加新命令 free(cmdHistory[0]); for(int i = 1; i < MAX_HST; i++){ cmdHistory[i-1] = cmdHistory[i]; } cmdHistory[MAX_HST-1] = strdup(getInput); } // 解析命令为参数 numCommand = 0; args[numCommand] = strtok(getInput, " \n"); while(args[numCommand] != NULL){ numCommand++; args[numCommand] = strtok(NULL, " \n"); } args[numCommand] = NULL; if(strcmp(args[0], "exit") == 0){ should_run = 0; break; } int is_background = 0; // 判断是否为后台命令 if (numCommand > 0 && strcmp(args[numCommand-1], "&") == 0) { is_background = 1; args[numCommand-1] = NULL; // 移除&参数 } pid_t pid = fork(); if(pid < 0){ fprintf(stderr, "Fork Failed\n"); return 1; } else if(pid == 0){ // 子进程执行命令 if(execvp(args[0], args) == -1){ fprintf(stderr, "Command Not Found.\n"); exit(1); } } else { // 父进程处理:前台命令等待子进程,后台命令不等待 if (!is_background) { wait(NULL); } else { printf("[Background process] pid: %d\n", pid); } } } // 释放历史命令内存 for(int i = 0; i < cmdHst; i++){ free(cmdHistory[i]); } return 0; }
修复说明
- 修复了提示符位置错误:父进程等待前台子进程执行完毕后才回到循环打印提示符,确保输出顺序正确。
- 修正
!!命令行为:现在!!会正确重复上一个命令,而非打印所有历史。 - 正确处理后台命令:带
&的命令会在后台运行,父进程不等待,同时移除&参数避免传给执行命令。 - 修复了空输入和EOF的处理,避免程序崩溃。
内容的提问来源于stack exchange,提问作者Cmcbride
相关产品推荐
相关产品推荐

