在C中将文件路径解析为可执行文件

Resolving filepath to executable in C

我目前的任务是用 C 语言编写原语 shell,但在实现 shell 功能时遇到了困难,该功能构建了给定请求程序的路径。例如将 wc 的用户输入转换为 /usr/bin/wc

Getenv() 可以很好地获取 $PATH 的值。使用我的导师提供的代码,我还将此值解析为单个 'tokens,',其中定义了一个标记:typedef char *tok_t

我的问题是如何修复以下函数的实现,如果找到,它寻求 return 给定文件名的绝对路径,否则 NULL

这里的主要问题是连接 tok_tchar* 以生成完整路径名。

char *resolve_path(char *filename) {
    printf("trying to resolve path...\n");
    char *path_var = getenv("PATH");
    tok_t *path_list = get_toks(path_var);
    //fprint_tok(stdout, path_list);
    char *path;
    for (int i = 0; path_list[i]; i++) {
       path = (char*) malloc(PATH_MAX);
       strcat(path, *path_list[i]);
       strcat(path, filename);
       printf("Trying... %s\n", path);
       if (file_exists(path)) {
           return path;
       }
       free(path);
    }
    return NULL;
}

我应该为 malloc() 和 strcat() 而烦恼,还是有更好的实现方法?目前正在获取有关使用 strcat() 的类型兼容性的段错误和警告。

您确实需要使用 malloc(),因为您要从函数返回结果路径(指向在此函数中创建的自动数组的指针在函数 returns 之后将无效)。您确实需要使用 strcat() 或类似的方法来生成一个连续的 char * 以传递给 file_exists().

但是您的代码存在一些问题:

  • 不要将 void * 显式转换为 C 中的其他类型 - 充其量是不必要的 (I'm talking about casting the return value of your allocation, in this case)。
  • 检查 malloc() 是否失败。
  • 您不需要在循环内调用 malloc()free() - 在循环外(每个)一次就足够了。
  • 如果 tok_tchar *,那么 path_listchar **,因此当您将 path_list[i] 传递给 [=24] 时,无需取消引用 path_list[i] =]/strncat(),因为当他们期望字符串时,那只会是 char。这可能是你的陈述 "Currently getting segfaults and warnings about the type compatibility in use of strcat()."
  • 您需要在第一次调用 strcat() 之前将 path 的第一个字符设置为 NULL,或者更好的是,使用 strncpy(),在这种情况下您将完成后想将 path 的最后一个字符设置为 NULL。
  • strncat()PATH_MAX - strlen(path) 一起使用,否则可能会溢出 path

这是一个例子:

char *resolve_path(const char *filename) {
    printf("trying to resolve path...\n");
    char *path_var = getenv("PATH");
    tok_t *path_list = get_toks(path_var);
    char *path = malloc(PATH_MAX+1); // See, no cast, and the +1 is for the NULL
    if (!path) {
        return NULL; // Check for failure
    }
    // strncpy/strncat won't null-terminate path if we run out of space
    path[PATH_MAX] = NULL;
    for (int i = 0; path_list[i]; i++) {
       // this could be done more efficiently with memcpy/hand-coding/etc
       strncpy(path, path_list[i], PATH_MAX); // don't dereference it
       strncat(path, filename, PATH_MAX - strlen(path));
       printf("Trying... %s\n", path);
       if (file_exists(path)) {
           return path;
       }
    }
    free(path);
    return NULL;
}

我正在阐述 因为按原样使用该代码仍然会导致分段错误。

因为 tok_t 只是 char* 的 typedef,它有点混淆你的代码。 get_toks() 是(没有 typedef 的)在技术上只是返回一个 char**.

这意味着这一行:

strncpy(path, *path_list[i], PATH_MAX);

其实应该是

strncpy(path, path_list[i], PATH_MAX);

因为您不应该取消引用 path_list[i]。如果这样做,当它需要 char* 时,您将 char 传递给 strncpy。这就是您收到该警告和段错误的原因。

因此,无论其价值如何,这里是我对您的代码的更正:

char *resolve_path(const char *filename) {
    printf("trying to resolve path...\n");
    char *path_var = getenv("PATH");
    tok_t *path_list = get_toks(path_var);
    char *path = malloc(PATH_MAX+1);
    if (path == NULL) {
        return NULL; // malloc failed
    }
    path[PATH_MAX] = '[=12=]'; // null terminate path
    for (int i = 0; path_list[i]; i++) {
       size_t len =  strlen(path_list[i]); // get the length of this path
       strncpy(path, path_list[i], PATH_MAX); // copy path_list to path
       path[len] = '/'; // add seperator
       strncat(path, filename, PATH_MAX - len - 1); // -1 because of separator
       printf("Trying... %s\n", path);
       if (file_exists(path)) {
           return path;
       }
    }
    free(path);
    return NULL;
}