对字符串进行标记,将其作为char *传递给execve()

时间:2014-07-17 06:25:38

标签: c pointers tokenize arrays execve

我对C的了解非常有限。我试图将从客户端传递给服务器的String标记化,因为我想使用传递给execve的参数。通过buffer传递的参数需要复制到*argv并进行标记,以便可以使用bufferargv[0]访问argv[1]个令牌,等。显然我做错了什么。

n = read(sockfd, buffer, sizeof(buffer));
strcpy(*argv, buffer);
printf("buffer:%s\n", buffer);
printf("argv:%s\n", *argv);
printf("argv[0]:%s\n", argv[0]);
printf("argv[1]:%s\n", argv[1]);
*argv = strtok_r(*argv, " ", argv);
printf("argv:%s\n", *argv);

i = fork();
if (i < 0) {
    //Close socket on fork error.
    perror("fork");
    exit(-1);
} else if (i == 0) {
    //execve on input args
    execve(argv[0], &argv[0], 0);
    exit(0);
} else {
    wait(&status);
    //close(sockfd);
}

传递参数&#34; / bin / date -u&#34;使用上面的代码给出了输出:

buffer:/bin/date -u

argv:/bin/date -u

argv[0]:/bin/date -u

argv[1]:(null)

我的输出是什么:

buffer:/bin/date -u

argv:/bin/date -u

argv[0]:/bin/date

argv[1]:-u

我尝试使用strtok_r(),但它没有像我预期的那样工作。我插入的片段是:

*argv = strtok_r(*argv, " ", argv);
printf("argv:%s\n", *argv);

,输出为argv:/bin/date

先谢谢了,SO。

编辑:我不必像上面那样明确地标记buffer。任何从客户端获取参数的方法都可以正常工作。

2 个答案:

答案 0 :(得分:3)

嗯,您正在处理几个问题。第一个是选择argv作为你正在编写缓冲区的可变量。虽然它只是一个指针数组,但您通常会将argv视为保存参数的数组传递给即时进程,而不是作为要修改的变量。然而,这确实是语义学,我知道没有禁止这样做。但是,您无法在将令牌分配给*argv的同时对*argv进行标记,因为strtok_r会在此过程中修改*argv

除此之外,真正的问题似乎是使用strtok_r。看看man strtok_r。为了对字符串进行标记,您需要对strtok_r进行重复调用,以便提取所有标记。使用第一个参数(* argv ...)对strtok_r的第一次调用仅提取第一个标记。为了完成提取,您必须将NULL作为第一个参数传递,直到所有标记都被提取为止。此外,您从中提取标记的字符串将通过调用strtok_r进行修改,并且不应在提取后使用。通常,如果稍后需要,则会生成字符串的副本以保留原始字符串。

在您的代码中,您只需拨打strtok_r一次E.g:

*argv = strtok_r(*argv, " ", argv);  // extracts the first token and modifies *argv

如果您的目的是提取所有字符串,那么您需要重复调​​用strtok_r之类的内容:

char *token = malloc (sizeof (token) * 128); // or something large enough to hold the tokens

token = strtok_r(*argv, " ", argv);
if (token)
    printf (" token: %s\n", token);

while ((token = strtok_r (NULL, " ", argv)) != NULL)
{
    printf (" token: %s\n", token);
}

您可以随意捕获各个令牌,以便将它们传递给execve。但是,在argv写回argv的同时,您无法从argv中剥离出来。如上所述,strtok_r在提取期间由{{1}}修改,因此您需要一个单独的数组来保存令牌。希望这会有所帮助。

答案 1 :(得分:0)

strtok()strtok_r()函数一次返回一个令牌。它们在调用之间保持状态,您需要在循环中调用它们以将字符串拆分为标记。他们还修改了作为第一个参数就地传递的缓冲区,因此你需要复制它。

让我举个例子:

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

#define MAX_CMD_SIZE 1024
#define MAX_ARG_COUNT 10

main()
{
    const char *command = "/bin/test arg1 arg2 arg3 arg4 arg5";

    /* Allocate a buffer for tokenization.
     * the strtok_r() function modifies this buffer in-place and return pointers
     * to strings located inside this buffer. */
    char cmd_buf[MAX_CMD_SIZE] = { 0 };
    strncpy(cmd_buf, command, sizeof(cmd_buf));

    /* This strtok_r() call puts '\0' after the first token in the buffer,
     * It saves the state to the strtok_state and subsequent calls resume from that point. */
    char *strtok_state = NULL;
    char *filename = strtok_r(cmd_buf, " ", &strtok_state);
    printf("filename = %s\n", filename);

    /* Allocate an array of pointers.
     * We will make them point to certain locations inside the cmd_buf. */
    char *args[MAX_ARG_COUNT] = { NULL };

    /* loop the strtok_r() call while there are tokens and free space in the array */
    size_t current_arg_idx;
    for (current_arg_idx = 0; current_arg_idx < MAX_ARG_COUNT; ++current_arg_idx) {
        /* Note that the first argument to strtok_r() is NULL.
         * That means resume from a point saved in the strtok_state. */
        char *current_arg = strtok_r(NULL, " ", &strtok_state);
        if (current_arg == NULL) {
            break;
        }

        args[current_arg_idx] = current_arg;
        printf("args[%d] = %s\n", current_arg_idx, args[current_arg_idx]);
    }
}

以上示例的输出为:

filename = /bin/test
args[0] = arg1
args[1] = arg2
args[2] = arg3
args[3] = arg4
args[4] = arg5

请注意,我将filenameargs放入单独的变量中,以说明第一个调用和后续调用之间的区别。对于execve(),您通常希望将它们放入单个数组中,并将其称为execve(argv[0], argv, NULL);,因为文件名应该是argv中的第一个元素。