在共享库搜索路径中查找目录:另一种解决方案?

Find a directory in shared library search path : Another solution?

九年前,有人问过这个问题:在共享库搜索路径中查找目录 (Find a directory in shared library search path)。

已使用以下方式给出答案:opendir() 然后 readdir() 然后 dlopen() ...

现在有没有更简单的方法,还是我仍然应该遵循这个 SMOP?

不,使用scandir() or glob()更合适。

事实上,opendir()/readdir()/closedir() 基本上从来没有 是 POSIXy 系统中任何东西的推荐方式,例如 Linux 具有 glob()scandir()nftw(),因为 home-spun opendir()/readdir()/closedir() 几乎从不处理重命名文件或目录的情况,在扫描过程中删除、创建或移动;而 POSIX C 库函数应该可以优雅地处理这些问题。

opendir()/readdir()/closedir() 如此受推崇的唯一原因是它们是在 C 标准中定义的(相对于 POSIX),因此可以在非 POSIXy 系统也是如此。但是,在我看来,仅仅因为某些系统的 C 库有缺陷,并不是一次又一次重新发明一个坏轮子的好理由;我们已经有了更好的工具。


例如,假设您构建了一个 glob 模式数组(例如,"/usr/lib/myapp/plugins/*.so", "/home/username/.config/myapp/plugins/*.so", NULL),并且您想要找到与这些模式匹配的文件。为此,您使用 glob()。例如:

#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <glob.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

int report_error(const char *path, int errnum)
{
    fprintf(stderr, "%s: %s.\n", path, strerror(errnum));
    return -1;
}

void report_glob_error(int result)
{
    switch (result) {
    case GLOB_NOSPACE: fprintf(stderr, "%s.\n", strerror(ENOMEM)); return;
    case GLOB_ABORTED: fprintf(stderr, "%s.\n", strerror(EIO));    return;
    case GLOB_NOMATCH: fprintf(stderr, "%s.\n", strerror(ENOENT)); return;
    }
}

int main(int argc, char *argv[])
{
    glob_t  matches;
    size_t  i;
    int     arg, result;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        const char *argv0 = (argc > 0) ? argv[0] : "(this)";
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
        fprintf(stderr, "       %s PATTERN [ PATTERN ... ]\n", argv0);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    result = glob(argv[1], GLOB_ERR | GLOB_MARK, report_error, &matches);
    if (result) {
        report_glob_error(result);
        return EXIT_FAILURE;
    }

    for (arg = 2; arg < argc; arg++) {
        result = glob(argv[arg], GLOB_ERR | GLOB_MARK | GLOB_APPEND, report_error, &matches);
        if (result) {
            report_glob_error(result);
            return EXIT_FAILURE;
        }
    }

    printf("Found %zu matches:\n", matches.gl_pathc);
    for (i = 0; i < matches.gl_pathc; i++) {
        printf("    %s\n", matches.gl_pathv[i]);
    }

    globfree(&matches);

    return EXIT_SUCCESS;
}

编译并 运行 以上内容,例如'/lib*/*/*.so' 作为参数。请记住将模式放在单引号中,否则 shell 会扩展它们。

如果您想要特定目录中的所有文件,或者需要更复杂的文件名过滤器(例如,替代 glob 模式;您可以使用 fnmatch() 根据 glob 模式检查名称),您可以使用scandir().

请注意,如果 glob 模式不够,您可以使用 POSIX 正则表达式,通过 regcomp()/regexec()/regfree().


关于使用 scandir() 的示例,我刚刚发布了一个 ,以回答类似的问题。