在C中将字符串解析为令牌 - 出了什么问题?

时间:2016-11-15 21:27:52

标签: c arrays argv strsep

我正在尝试将字符串拆分为标记以创建参数参数数组。我当前的实现如下(path是用于读取可选参数的用户可执行文件的路径:

// ARG_MAX as defined in limits.h
int execute(char *exe) {
    printf("args to %s: ", exe);

    char *args = malloc(ARG_MAX);
    scanf("%s", args);

    char *argv[ARG_MAX];

    int i = 0;
    argv[i++] = exe;

    while ((argv[i] = strsep(&args, " \t")) != NULL) {
        i++;
    }

    free(args);
    execv(exe, argv);
    return 0;
}

让我感到困惑的是,根据我对strsep的理解,这应该按预期工作,并且在某种程度上确实如此,在测试时它会准确地将tokens[0]分配为path,并且tokens[1]是第一个空格字符之前的tokens_s

但是,如果在空格之后输入另一个参数,则不会将其分配到tokens[2]中,依此类推后续参数。

在使用不会导致所需功能的strsep时,我似乎无法发现我做错了什么?

输入: exe = "/usr/bin/ps" args = "-e -l"

输出: exec ps -e

1 个答案:

答案 0 :(得分:3)

多个错误:

  • 您必须阅读fgets()的参数才能阅读多个单词。

  • 您必须为strsep()使用临时变量,以便将原始指针从malloc()传递回free(),或者只使用本地数组。

以下是更正后的版本:

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

// ARG_MAX as defined in limits.h
int execute(char *exe) {
    char args[ARG_MAX + 1];

    printf("args to %s: ", exe);
    fflush(stdout);
    if (fgets(args, sizeof args, stdin)) {
        char *argv[ARG_MAX / 2];
        char *p;

        int i = 0;
        argv[i++] = exe;

        p = args;
        args[strcspn(args, "\n")] = '\0';  // strip the newline if present
        while ((argv[i] = strsep(&p, " \t")) != NULL) {
            i++;
        }

        printf("argv: ");
        for (i = 0; argv[i]; i++)
            printf(" '%s'", argv[i]);
        printf("\n");

        execv(exe, argv);
        printf("exec failed: %s\n", strerror(errno));
    } else {
        printf("cannot read input\n");
    }
    return 0;
}

int main(int argc, char *argv[]) {
    char *exe = "printf";
    if (argc > 1)
        exe = argv[1];
    return execute(exe);
}

注意:

    如果成功,
  • execv将无法返回您的计划。

  • strsep不会折叠分隔符序列,如果您有额外的空格,您的方法将创建额外的参数。

编辑:如果在您运行stdin之前从execute读取输入,并且如果通过调用scanf()执行此类输入,则可能是stdin缓冲区中的待处理换行符,fgets()会将其读为空行。如果是这种情况,请先调用挂起的输入,然后再调用printf()

int c;
while ((c = getchar()) != EOF && c != '\n') {
    continue;
}