Обработка аргументов в пользовательской оболочке

В настоящее время я работаю над оболочкой, реализованной на C, для класса, который я надеюсь использовать со временем, и столкнулся с проблемой при выполнении моих аргументов. Моя программа использует 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] = "\0";
}

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] = "       \0";
  argv[1] = "       \0";

  }*/

Пример вывода:

?: ls -l
//functions as intended

?:echo
//argument returns as echol

Это относится к любой записи, которая короче предыдущей записи. Я не понимаю, почему exec читает аргумент, продолжающийся после '\0', и я уверен, что здесь я делаю ошибку памяти. Помощь будет очень признательна, я застрял на этом в течение нескольких дней.


person Chuckles    schedule 30.09.2015    source источник
comment
Если бы вы могли объяснить мне, почему вы используете глобальные переменные...   -  person Iharob Al Asimi    schedule 01.10.2015
comment
Я отредактировал ваш вопрос. Ваша ссылка на оболочку C может сбить с толку; это обычно относится к существующей оболочке csh.   -  person Keith Thompson    schedule 01.10.2015
comment
Используйте прототипы! И не переводите результат malloc & friends в C!   -  person too honest for this site    schedule 01.10.2015
comment
Я удалил глобальные переменные в соответствии с вашей рекомендацией, и спасибо, Кит, я не знал об этом!   -  person Chuckles    schedule 01.10.2015


Ответы (2)


Вам нужно указать конец массива argv элементом, который содержит нулевой указатель. Закомментированная строка:

argv[j] = "\0";

должно быть:

argv[j] = NULL;

Вам также необходимо поставить нулевой терминатор в конце строки argument. Вы получаете l, когда выполняете echo, потому что argument все еще содержит предыдущую командную строку. Итак, первая строка устанавливает argument в:

ls -l

Затем вы перезаписываете первые 4 символа на echo, так что получается:

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] = '\0';
    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.

person Barmar    schedule 30.09.2015
comment
Спасибо за такое понятное объяснение! Я полностью упускал из виду спор как проблему. - person Chuckles; 01.10.2015

После цикла, который читает входную строку, вам нужно завершить строку символом NUL.

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

Вам также нужно сделать то, что сказал @Barmar, но это другая проблема, чем та, которую вы описываете в вопросе.

person user3386109    schedule 30.09.2015