1

My knowledge of C is very limited. I'm trying to tokenize a String passed to a server from a client, because I want to use passed arguments toexecve. The arguments passed viabufferneeds to be copied to*argv and tokenized such thatbuffer's tokens can be accessed withargv[0], argv[1], etc. Obviously I'm doing something incorrectly.

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);
}

Passing the arguments "/bin/date -u" with the above code gives an output of:

buffer:/bin/date -u

argv:/bin/date -u

argv[0]:/bin/date -u

argv[1]:(null)

What I what is an output of:

buffer:/bin/date -u

argv:/bin/date -u

argv[0]:/bin/date

argv[1]:-u

I tried usingstrtok_r(), but it didn't work as I intended. The snippet I inserted was:

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

which give an output of argv:/bin/date.

Thanks in advanced, SO.

Edit: I don't have to explicitly tokenizebufferlike I have above. Any way to get arguments from the client passed to the server works fine.

4

2 回答 2

3

好吧,您正在处理几个问题。第一个是选择argv作为您正在写入缓冲区的变量。虽然它只是一个指针数组,但您通常将其视为包含传递argv即时进程的参数的数组,而不是要修改的变量。但是,这确实是语义,据我所知,没有禁止这样做。但是,您不能在分配令牌的同时进行令牌化,因为在此过程中会进行修改。*argv*argvstrtok_r*argv

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

在您的代码中,您strtok_r只调用一次例如:

*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,因此您将需要一个单独的数组来保存令牌。希望这可以帮助。

于 2014-07-17T07:46:12.103 回答
0

strtok()andstrtok_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.

于 2014-07-17T08:03:55.673 回答