Попытка использовать execvp () в C с пользовательским вводом в Unix - PullRequest
3 голосов
/ 24 августа 2009

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

Например, если бы они дали мне "ls -la", мне пришлось бы выполнить эту команду. Я пробовал следующий код:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{

    int ret, num_args;

    printf("Enter number of arguments (Example: \"ls -la\" has 1 argument): ");
    scanf("%d", &num_args);

    char *cmd[num_args];

    printf("Enter command name: ");
    scanf("%s", &cmd[0]);

    int i;
    for (i = 0; i < num_args; i++)
    {
            printf("Enter parameter: ");
            scanf("%s", &cmd[i]);
    }

    execvp(cmd[0], cmd);
}

Однако, когда я попробовал следующий прогон, он дал мне «ошибку сегментации»

$ ./a.out 
Enter number of arguments (Example: "ls -la" has 1 argument): 2
Enter command name: ls
Enter parameter: -la
Enter parameter: .
Segmentation fault
$

Есть идеи?

Ответы [ 5 ]

3 голосов
/ 24 августа 2009

Если ваша реализация поддерживает это, вы должны использовать более безопасный getline() вместо scanf() или fgets(). getline() будет безопасно обрабатывать длинные строки и символы NULL. Он выделит достаточно памяти для размещения всей строки. getline() может выделить память, так что позже вам придется ее самостоятельно освободить.

Вот документация glibc getline() .

Вот быстрая модификация для использования getline (она все еще нуждается в работе, проверке ошибок, и я еще не полностью проверил ее на корректность):

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{

    printf("Enter number of arguments (Example: \"ls -la\" has 1 argument): \n");

    char *num = NULL;
    size_t sz = 0;
    getline(&num, &sz, stdin);

    int num_args;
    sscanf(num, "%d", &num_args);

    char *cmd[num_args+2];
    memset(cmd, 0, sizeof(char*) * (num_args+2));

    printf("Enter command name: \n");


    int len = getline(&cmd[0], &sz, stdin); 

    cmd[0][len-1] = '\0';

    int i;
    for (i = 1; i < num_args+1; i++)
    {
        printf("Enter parameter: \n");
        sz = 0;
        len = getline(&cmd[i], &sz, stdin);
        cmd[i][len-1] = '\0';
    }

    return execvp(cmd[0], cmd);

}
3 голосов
/ 24 августа 2009

Вам нужно выделить память для ваших строк. Следующая строка выделяет num_args указателей на char:

char *cmd[num_args];

Прежде всего, вы получите num_args + 1 строк (не забывайте, что сама команда cmd[0]). Самый простой способ - статически выделить память в виде массива символьных буферов:

const unsigned int MAX_LEN = 512; // Arbitrary number
char cmd[num_args + 1][MAX_LEN];

Однако теперь вы не можете использовать scanf для чтения в строке, потому что пользователь может ввести строку, которая длиннее, чем ваш символьный буфер. Вместо этого вам придется использовать fgets, что может ограничить количество символов, которые может ввести пользователь:

fgets(cmd[i], MAX_LEN, stdin);

Имейте в виду, что fgets также считывает символы новой строки, поэтому обязательно удалите все случайные символы, которые появляются (но не думайте, что они есть).

2 голосов
/ 24 августа 2009

Кроме того, вам нужна еще одна запись в argv, которую вы передаете execvp, которая должна быть (char *)NULL, чтобы сообщить, что она достигла конца списка.

1 голос
/ 24 августа 2009

Вы фактически не выделили памяти для строк, на которые указывает массив cmd.

0 голосов
/ 24 августа 2009

Взгляните на справочную страницу для scanf (). Одна из самых приятных вещей, которые он может сделать, - это автоматически распределять строковые буферы на лету, вам нужно указать указатель на строку, а не просто передать строку и указать% как формат.

char *my_string;
scanf("%as", &my_string);

Тогда вам не нужно беспокоиться о предварительном распределении, не нужно беспокоиться о переполнении буфера и т. Д. Просто не забывайте освобождать () его после того, как с ним покончено.

...