假设给定的目录树大小合理:比如像Twisted或Python这样的开源项目,那么遍历和遍历该目录中所有文件/目录的绝对路径的最快方法是什么?
我想从Python中做到这一点。 os.path.walk 很慢。所以我尝试了 ls -lR 和 tree -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位二进制tree.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
答案 0 :(得分:3)
即使os.path.walk
没有花时间,你在pf中的方法也会毫无希望地缓慢。在所有现存路径上进行包含3个无界闭包的正则表达式匹配将会在那里杀死你。以下是我引用的Kernighan and 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之前编写的,当一个读取目录文件为raw时甚至可以想象。代码的方法远比所有指针吊索都有用。
答案 1 :(得分:1)
在性能方面很难比find
好得多,但问题是速度有多快,为什么你需要这么快?你声称os.path.walk很慢,实际上,它在我的机器上比16k目录的树慢约3倍。但话说回来,我们谈论的是Python的0.68秒和1.9秒之间的差异。
如果设定速度记录是您的目标,那么您无法击败硬编码C,这完全是75%的系统调用限制,您无法使操作系统更快。也就是说,25%的Python时间花在了系统调用上。你想用遍历的路径做什么?
答案 2 :(得分:0)
你没有提到的一个解决方案是'os.walk'。我不确定它会比os.path.walk更快,但它客观上更好。
当你拥有目录列表时,你还没有说明你要做什么,所以很难给出更具体的建议。
答案 3 :(得分:0)
虽然我怀疑你有多个读头,但这里是你如何遍历几百万个文件(我们在几分钟内完成了10M +)。
https://github.com/hpc/purger/blob/master/src/treewalk/treewalk.c