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

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;

}

问题根源与修复方案

  1. 父进程未等待前台子进程完成

    • 原代码中父进程fork后直接回到循环,会先打印osh> 提示符,而子进程的输出后出现,导致格式混乱。且子进程中调用wait(NULL)完全错误,子进程不应等待自身。
    • 修复:父进程判断是否为后台命令(带&),前台命令则等待子进程结束,后台命令则不等待,同时要从参数中移除&避免传给execvp。
  2. !!命令逻辑错误

    • 原代码会打印所有历史命令并覆盖getInput,且仍会将!!加入历史,不符合!!重复上一个命令的预期。
    • 修复:!!仅加载上一个历史命令到getInput,无历史时提示后跳过后续执行,且将实际执行的命令加入历史。
  3. 循环条件错误

    • 原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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:05:19