execvp возвращает ошибку Нет такого файла или каталога после передачи ему массива строк из strtok

Я работал над заданием по написанию оболочки на C, и в настоящее время я застрял с чем-то, что, как я считаю, должно быть довольно просто исправить, но я просто не вижу, в чем ошибка. Я извлек основные части своего кода, чтобы продемонстрировать проблему.

Идея состоит в том, что я получаю от пользователя строку, которая делится на отдельные строки, затем вставляется в массив, который затем передается execvp для выполнения.

Вы можете видеть, что я жестко запрограммировал некоторые значения для char *arguments[2]. Если я использую это, как в execvp, например execvp(arguments[0], arguments), он выполняет «ls -l» и работает отлично.

Однако проблема возникает, когда я позволяю tokenizeInput обрабатывать любую строку, которую я ввожу. В этом примере жестко запрограммировано как «ls -l». Это довольно просто, toknizer разделяет его с помощью разделителя " " и присваивает каждое значение моему массиву указателей на символы. Я распечатываю значения, чтобы проверить, верны ли они, что, кажется, так и есть. И затем я возвращаю массив.

Итак, теперь в основном я должен получить свой массив, прошедший через токенизатор. Его содержимое не должно отличаться от содержимого char *arguments[2]. Но когда я его использую, я просто получаю сообщение об ошибке «Нет такого файла или каталога». Я распечатывал массив в main и, похоже, не заметил в них ничего плохого.

Я читал руководство по execvp и strtok. Я почти уверен, что и массив, и строки внутри них завершаются нулем, как того требует execvp.

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

int main(int argc, char *argv[]) {

    /*This works fine!*/
    char *arguments[2]; 
    arguments[0] = "ls"; 
    arguments[1] = "-l";
    arguments[2] = NULL; /*null terminating*/
    /*This does not work...*/
    char **inputs; 
    inputs = tokenizeInput("ls -l");    
    execvp(inputs[0], inputs);
    perror("Error");
    return 0;
}

char** tokenizeInput(char *input) {
    char newInput[70]; 
    strcpy(newInput, input); 
    char **array = malloc(4 * sizeof(newInput)); /*memory pls*/

    int i = 0; 
    array[i] = strtok(newInput, " ");
    while(array[i] != NULL) {
        printf("Putting string: %s into array[%i]\n", array[i], i);
        array[++i] = strtok(NULL, " "); /*Returns next token for each loop*/
    }

    printf("First argument: %s. ", array[0]); /*Should be "ls"*/
    printf("Second argument: %s. ", array[1]); /*Should be "-l"*/
    printf("Third argument: %s. ", array[2]); /*Should be NULL*/

    return array;
}

person swebonny    schedule 19.05.2014    source источник


Ответы (1)


Проблема заключается в том, что вы копируете входную строку в массив, локально выделенный в стеке (массив newInput). Функция strtok() просто возвращает указатели на этот массив newInput.

В вашей функции tokenizeInput() все работает нормально, но когда вы возвращаетесь из функции, стек извлекается, а затем повторно используется для других целей, так что ваши данные могут быть перезаписаны.

Вместо этого попробуйте выделить массив newInput. Не забудьте освободить память, когда закончите!

Изменить: вместо malloc() можно использовать strdup(), чтобы не беспокоиться о проверке длины ввода. Но если вы не уверены, что первый символ не является пробелом, операция free() может немного запутаться. В качестве альтернативы вы можете strdup() каждое слово, которое входит в массив, а затем освободить элементы массива, когда вы закончите.

person hetman    schedule 19.05.2014
comment
Большое спасибо! Я взгляну. Как ни странно, мой код каким-то образом работает на компьютерах SunOS 5.1 моего университета. Однако не в моей Windows (Cygwin + gcc). - person swebonny; 19.05.2014
comment
Я полагаю, что «SunOS 5.1» — это опечатка либо для 5.10, либо для 5.11. SunOS 5.1 уже давно устарела — я скорее думаю, что она была актуальна в прошлом тысячелетии, а не в нынешнем. - person Jonathan Leffler; 19.05.2014