处理自定义参数 shell

Handling arguments in a custom shell

我目前正在为 class 开发一个用 C 语言实现的 shell,我希望随着时间的推移在其基础上构建 运行 并在执行我的论点时遇到问题。我的程序使用 getchar() 将条目解析为参数数组,然后使用 execvp() 执行参数。我遇到的问题是在重复输入参数时,任何后续较短的参数都会与内存中某处留下的字符连接起来。下面的例子。我需要使用 getchar,这排除了获取参数的替代方法。

//Global Variables    
char argument[64];  
char **argv;

int main(int argc, char** argv) {
    mainloop();                      //Loop that calls the commands
    return (EXIT_SUCCESS);
}

void prompt(){                       //Modular prompt
    printf ("?:");
}

void mainloop(){                    //Loop that calls the functions
    while(1){
        prompt();
        argv = (char**)malloc(sizeof(char*)*64);  //allocate memory for argv
        getcommand(argument, argv);
        if((strcmp(argv[0],"exit" )) == 0){  //check for exit
            return 0;
        }
        executecommand();
        printcommand();
        //clearcommand();
        //printcommand();
    }
}

void getcommand(char* argument, char** argv){  //Parser for the command
    int i=0,j=0;
    char c;
    char* token;
    while((c = getchar()) != '\n' ){ //gets char and checks for end of line
        argument[i] = c;
        i++;
    }
    token = strtok(argument, " ,.");  //tokenize the command
    while (token != NULL){ 
        argv[j] = token;   //pass command to array of arguments
        token = strtok(NULL, " ,.");
        j++;
    }
    //argv[j] = "[=11=]";
}

void executecommand(){  //Function to call fork and execute with errors
    pid_t childpid = fork();
    int returnStatus;
    if(childpid == -1){                           //Fail to Fork
        printf("failed to fork");
        exit(1);
    }
    else if(childpid == 0){                      //Child process
        if (execvp(*argv, argv) < 0){
            printf("error executing\n");
            exit(1);
        }
        else{                                   //Execute successful
            printf("executed");
        }
    }
    int c=(int)waitpid(childpid, &returnStatus, 0);
    if (returnStatus == 0)  // Verify child process terminated without error.  
        {
            printf("The child process terminated normally. \n");   
        }

    if (returnStatus == 1)      
        {
            printf("The child process terminated with an error!.\n");    
        }
    //realloc(argv,64);
}

void printcommand(){  //Test function to print arguments
    int i = 0;
    while(argv[i] != NULL){
        printf("Argv%d: %s \n",i, argv[i] );
        i++;
    }
}

/*void clearcommand(){     //Function to clear up the memory, does not work
  int i=0;
  argv[0] = "       [=11=]";
  argv[1] = "       [=11=]";

  }*/

示例输出:

?: ls -l
//functions as intended

?:echo
//argument returns as echol

任何比前一个条目短的条目都是这种情况。我不明白为什么 exec 在 '[=16=]' 之后继续读取参数,我确信我在这里犯了一个内存错误。非常感谢您的帮助,我已经坚持了几天了。

您需要使用包含空指针的元素来指示 argv 数组的结尾。注释掉的行:

argv[j] = "[=10=]";

应该是:

argv[j] = NULL;

您还需要在 argument 字符串的末尾放置一个空终止符。当您执行 echo 时,您会得到 l,因为 argument 仍然包含之前的命令行。所以第一行设置 argument 为:

ls -l

然后你用echo覆盖前4个字符,所以它变成:

echol

所以完整的函数是:

void getcommand(char* argument, char** argv){  //Parser for the command
    int i=0,j=0;
    char c;
    char* token;
    while((c = getchar()) != '\n' ){ //gets char and checks for end of line
        argument[i] = c;
        i++;
    }
    argument[i] = '[=14=]';
    token = strtok(argument, " ,.");  //tokenize the command
    while (token != NULL){ 
        argv[j] = token;   //pass command to array of arguments
        token = strtok(NULL, " ,.");
        j++;
    }
    argv[j] = NULL;
}

您也可以使用 fgets() 读取一行输入,而不是自己调用 getchar()

您还应该检查输入是否大于 argument 的大小。

在读取输入行的循环之后,您需要使用 NUL 字符终止该行。

while((c = getchar()) != '\n' ){ //gets char and checks for end of line
    argument[i] = c;
    i++;
}
argument[i] = '[=10=]';  // <<---- terminate the input line

您还需要按照@Barmar 所说的进行操作,但这与您在问题中描述的问题不同。