【问题标题】:Trying to use execvp() in C with user input in unix尝试在 C 中使用 execvp() 和 unix 中的用户输入
【发布时间】:2009-08-24 00:24:25
【问题描述】:

我正在尝试制作一个提示用户输入命令的程序,然后使用 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
$

有什么想法吗?

【问题讨论】:

    标签: c unix segmentation-fault execvp


    【解决方案1】:

    如果您的实现支持它,您应该在scanf()fgets() 上使用更安全的getline()getline() 将安全地处理长行和 NULL 字符。它将分配足够的内存以适应整行。 getline() 可以分配内存,所以你以后必须自己释放它。

    这里是glibc getline() documentation

    这里是使用 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);
    
    }
    

    【讨论】:

    • 编译器似乎不知道 getline 是什么,这是因为缺少#include 行还是只是编译器本身?
    • getline 是 C 中的 GNU 函数:gnu.org/s/libc/manual/html_node/Line-Input.html
    • 什么编译器和什么操作系统?
    • i686-apple-darwin9-gcc-4.0.1 (OS X 10.5.8)
    • 它说:未定义的符号:“_getline”,引用自:ccvpqdih.o 中的 _main ccvpqdih.o 中的 _main ccvpqdih.o 中的 _main ld:未找到符号 collect2:ld 返回 1 个退出状态
    【解决方案2】:

    您需要为字符串分配内存。以下行仅分配 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 也可以读取换行符,因此请确保删除所有出现的杂散字符(但不要假设它们在那里)。

    【讨论】:

    • 以这种方式尝试给了我一堆编译器错误。它说 cmd[num_arghs + 1][MAX_LEN];不对:“';'之前的语法错误”
    • @Nick 仔细检查您的代码;我写了一个小测试程序,它编译得很好。
    • 糟糕,我忘记了MAX_LEN = 512 行上的分号。现已修复。
    • scanf 还可以限制用户可以输入的字符数 - 如果您的缓冲区大小为 512,请使用 "%511s",如您的示例所示。
    • 哦,如果你这样做,你不能直接将cmd 传递给execvp - 它需要一个指向 char 的指针数组(由一个 NULL 条目终止),而不是一个char 数组(这是一个很好的例子,说明它们等效)。
    【解决方案3】:

    此外,您需要在传递给execvpargv 中再添加一个条目,它必须是(char *)NULL 才能让它知道它已到达列表末尾。

    【讨论】:

    • 另外一个用于 argv[0],它是命令的名称(通常)。
    【解决方案4】:

    您实际上并没有为 cmd 数组指向的字符串分配任何内存。

    【讨论】:

    • 那么我该怎么做呢?
    • 查找 malloc/free 并确保 "cmd" 中的每个指针都分配了足够的空间。
    【解决方案5】:

    查看 scanf() 的手册页。它可以做的最巧妙的事情之一是动态自动分配字符串缓冲区,您需要提供指向字符串的指针,而不是仅仅传递字符串并提供 %as 格式。

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

    那么你就不需要担心预分配,不需要担心缓冲区溢出等。只要记住在你完成后释放它。

    【讨论】:

    • '%as' 规范不是标准 C 行为 - 尽管它可能有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 2015-04-13
    • 1970-01-01
    相关资源
    最近更新 更多