4

假设给定的目录树大小合理:比如说像 Twisted 或 Python 这样的开源项目,遍历和迭代该目录内所有文件/目录的绝对路径的最快方法是什么?

我想从 Python 中做到这一点。os.path.walk很慢。所以我尝试了 ls -lRtree -fi。对于一个大约有 8337 个文件(包括 tmp、pyc、test、.svn 文件)的项目:

$ time tree -fi > /dev/null 

real    0m0.170s
user    0m0.044s
sys     0m0.123s

$ time ls -lR > /dev/null 

real    0m0.292s
user    0m0.138s
sys     0m0.152s

$ time find . > /dev/null 

real    0m0.074s
user    0m0.017s
sys     0m0.056s
$

tree似乎比 快ls -lR(虽然ls -R比 快tree,但它没有给出完整的路径)。find是最快的。

谁能想到更快和/或更好的方法?在 Windows 上,如有必要,我可能会简单地发布一个 32 位二叉树.exe 或 ls.exe。

更新 1:添加find

更新 2:我为什么要这样做?...我正在尝试对 cd、pushd 等进行智能替换,并为依赖传递路径的其他命令(更少、更多、cat、vim、tail)进行包装命令。该程序偶尔会使用文件遍历来执行此操作(例如:键入“cd sr grai pat lxml”会自动转换为“cd src/pypm/grail/patches/lxml”)。如果这个 cd 替换需要半秒钟来运行,我不会感到满意。见http://github.com/srid/pf

4

4 回答 4

3

即使os.path.walk完全没有时间,您在 pf 中的方法也会非常缓慢。在所有现有路径中进行包含 3 个无界闭包的正则表达式匹配会在此处杀死您。这是我引用的Kernighan 和 Pike的代码,这是该任务的正确算法:

/* spname:  return correctly spelled filename */
/*
 * spname(oldname, newname)  char *oldname, *newname;
 *  returns -1 if no reasonable match to oldname,
 *           0 if exact match,
 *           1 if corrected.
 *  stores corrected name in newname.
 */

#include <sys/types.h>
#include <sys/dir.h>

spname(oldname, newname)
    char *oldname, *newname;
{
    char *p, guess[DIRSIZ+1], best[DIRSIZ+1];
    char *new = newname, *old = oldname;

    for (;;) {
        while (*old == '/') /* skip slashes */
            *new++ = *old++;
        *new = '\0';
        if (*old == '\0')   /* exact or corrected */
            return strcmp(oldname,newname) != 0;
        p = guess;  /* copy next component into guess */
        for ( ; *old != '/' && *old != '\0'; old++)
            if (p < guess+DIRSIZ)
                *p++ = *old;
        *p = '\0';
        if (mindist(newname, guess, best) >= 3)
            return -1;  /* hopeless */
        for (p = best; *new = *p++; ) /* add to end */
            new++;                    /* of newname */
    }
}

mindist(dir, guess, best)   /* search dir for guess */
    char *dir, *guess, *best;
{
    /* set best, return distance 0..3 */
    int d, nd, fd;
    struct {
        ino_t ino;
        char  name[DIRSIZ+1];   /* 1 more than in dir.h */
    } nbuf;

    nbuf.name[DIRSIZ] = '\0';   /* +1 for terminal '\0' */
    if (dir[0] == '\0')     /* current directory */
        dir = ".";
    d = 3;  /* minimum distance */
    if ((fd=open(dir, 0)) == -1)
        return d;
    while (read(fd,(char *) &nbuf,sizeof(struct direct)) > 0)
        if (nbuf.ino) {
            nd = spdist(nbuf.name, guess);
            if (nd <= d && nd != 3) {
                strcpy(best, nbuf.name);
                d = nd;
                if (d == 0)     /* exact match */
                    break;
            }
        }
    close(fd);
    return d;
}

/* spdist:  return distance between two names */
/*
 *  very rough spelling metric:
 *  0 if the strings are identical
 *  1 if two chars are transposed
 *  2 if one char wrong, added or deleted
 *  3 otherwise
 */

#define EQ(s,t) (strcmp(s,t) == 0)

spdist(s, t)
    char *s, *t;
{
    while (*s++ == *t)
        if (*t++ == '\0')
            return 0;       /* exact match */
    if (*--s) {
        if (*t) {
            if (s[1] && t[1] && *s == t[1] 
              && *t == s[1] && EQ(s+2, t+2))
                return 1;   /* transposition */
            if (EQ(s+1, t+1))
                return 2;   /* 1 char mismatch */
        }
        if (EQ(s+1, t))
            return 2;       /* extra character */
    }
    if (*t && EQ(s, t+1))
        return 2;           /* missing character */
    return 3;
}

注意:此代码是在 ANSI C、ISO C 或 POSIX 之前编写的,当人们读取原始目录文件时,甚至可以想象任何事情。代码的方法比所有的指针吊起来有用得多。

于 2010-04-23T00:11:07.643 回答
1

很难比find性能好得多,但问题是要快多少,为什么需要这么快?您声称 os.path.walk 很慢,实际上,在我的机器上,它比 16k 目录树慢约 3 倍。但话又说回来,我们谈论的是 Python 的 0.68 秒和 1.9 秒之间的差异。

如果设置速度记录是您的目标,那么您无法击败完全 75% 系统调用绑定的硬编码 C,并且您无法让操作系统运行得更快。也就是说,25% 的 Python 时间花在系统调用上。您想对遍历的路径做什么?

于 2010-04-22T23:42:15.900 回答
0

虽然我怀疑你有多个读头,但这里是你可以遍历几百万个文件的方法(我们在几分钟内完成了 10M+)。

https://github.com/hpc/purger/blob/master/src/treewalk/treewalk.c

于 2012-03-20T23:04:10.337 回答
0

您没有提到的一种解决方案是“os.walk”。我不确定它会比 os.path.walk 快,但客观上更好。

你还没有说当你拥有目录列表时你将如何处理它,所以很难给出更具体的建议。

于 2010-04-22T23:36:31.530 回答