当有大量文件(> 100,000)时,我正试图找出找到特定目录中文件数量的最佳方法.
当存在那么多文件时,执行"ls | wc -l"需要相当长的时间才能执行.我相信这是因为它返回了所有文件的名称.我试图占用尽可能少的磁盘IO.
我已经尝试了一些shell和Perl脚本无济于事.有任何想法吗?
mar*_*k4o 170
默认情况下,ls对名称进行排序,如果有很多名称,则可能需要一段时间.在读取和排序所有名称之前,也不会输出.使用该ls -f选项可关闭排序.
ls -f | wc -l
Run Code Online (Sandbox Code Playgroud)
请注意,这还将使-a,所以.,..和开始与其他文件.将被计算在内.
Chr*_*ltz 58
最快的方法是专门构建的程序,如下所示:
#include <stdio.h>
#include <dirent.h>
int main(int argc, char *argv[]) {
DIR *dir;
struct dirent *ent;
long count = 0;
dir = opendir(argv[1]);
while((ent = readdir(dir)))
++count;
closedir(dir);
printf("%s contains %ld files\n", argv[1], count);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
从我的测试中不考虑缓存,我对这个目录中的每一个都进行了大约50次,一遍又一遍,以避免基于缓存的数据偏斜,并且我得到了大致以下性能数字(在实际时钟时间内):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Run Code Online (Sandbox Code Playgroud)
最后一个dircnt是从上面的源编译的程序.
编辑2016-09-26
由于受欢迎的需求,我重新编写了这个程序是递归的,所以它将落入子目录并继续分别计算文件和目录.
由于很明显有些人想知道如何做到这一切,我在代码中有很多评论,试图让它显而易见.我写了这个并在64位Linux上测试它,但它应该适用于任何符合POSIX标准的系统,包括Microsoft Windows.欢迎提供错误报告; 如果你无法在AIX或OS/400上运行它,我很乐意更新它.
正如你所看到的,这是很多比原来的和必然如此复杂:至少一个功能必须存在递归调用,除非你想要的代码变得非常复杂(如管理一个子目录栈和处理,在一个循环中).由于我们必须检查文件类型,不同操作系统,标准库等之间的差异开始发挥作用,所以我编写了一个程序,试图在任何可以编译的系统上使用.
错误检查非常少,而且count函数本身并不真正报告错误.能够真正失败的唯一电话是opendir和stat(如果你不是幸运,有其中系统dirent包含已在文件类型).关于检查子路径名的总长度,我并不偏执,但理论上,系统不应允许任何长度超过的路径名PATH_MAX.如果有问题,我可以解决这个问题,但这只是需要向学习写C的人解释的更多代码.该程序旨在成为如何递归地潜入子目录的示例.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>
#if defined(WIN32) || defined(_WIN32)
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
/* A custom structure to hold separate file and directory counts */
struct filecount {
long dirs;
long files;
};
/*
* counts the number of files and directories in the specified directory.
*
* path - relative pathname of a directory whose files should be counted
* counts - pointer to struct containing file/dir counts
*/
void count(char *path, struct filecount *counts) {
DIR *dir; /* dir structure we are reading */
struct dirent *ent; /* directory entry currently being processed */
char subpath[PATH_MAX]; /* buffer for building complete subdir and file names */
/* Some systems don't have dirent.d_type field; we'll have to use stat() instead */
#if !defined ( _DIRENT_HAVE_D_TYPE )
struct stat statbuf; /* buffer for stat() info */
#endif
/* fprintf(stderr, "Opening dir %s\n", path); */
dir = opendir(path);
/* opendir failed... file likely doesn't exist or isn't a directory */
if(NULL == dir) {
perror(path);
return;
}
while((ent = readdir(dir))) {
if (strlen(path) + 1 + strlen(ent->d_name) > PATH_MAX) {
fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + 1 + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
return;
}
/* Use dirent.d_type if present, otherwise use stat() */
#if defined ( _DIRENT_HAVE_D_TYPE )
/* fprintf(stderr, "Using dirent.d_type\n"); */
if(DT_DIR == ent->d_type) {
#else
/* fprintf(stderr, "Don't have dirent.d_type, falling back to using stat()\n"); */
sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
if(lstat(subpath, &statbuf)) {
perror(subpath);
return;
}
if(S_ISDIR(statbuf.st_mode)) {
#endif
/* Skip "." and ".." directory entries... they are not "real" directories */
if(0 == strcmp("..", ent->d_name) || 0 == strcmp(".", ent->d_name)) {
/* fprintf(stderr, "This is %s, skipping\n", ent->d_name); */
} else {
sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
counts->dirs++;
count(subpath, counts);
}
} else {
counts->files++;
}
}
/* fprintf(stderr, "Closing dir %s\n", path); */
closedir(dir);
}
int main(int argc, char *argv[]) {
struct filecount counts;
counts.files = 0;
counts.dirs = 0;
count(argv[1], &counts);
/* If we found nothing, this is probably an error which has already been printed */
if(0 < counts.files || 0 < counts.dirs) {
printf("%s contains %ld files and %ld directories\n", argv[1], counts.files, counts.dirs);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编辑2017-01-17
我已经合并了@FlyingCodeMonkey建议的两个更改:
lstat而不是stat.如果您正在扫描的目录中有符号链接目录,这将更改程序的行为.以前的行为是(链接的)子目录将其文件计数添加到总计数中; 新行为是链接目录将计为单个文件,其内容将不计算在内.编辑2017-06-29
运气好的话,这将是这个答案的最后编辑:)
我已将此代码复制到GitHub存储库中,以便更容易获取代码(而不是复制/粘贴,您只需下载源代码),此外,任何人都可以通过提交拉动来更轻松地建议修改 - 从GitHub请求.
该源可在Apache License 2.0下获得.补丁* 欢迎!
igu*_*tin 34
你试过找吗?例如:
find . -name "*.ext" | wc -l
Run Code Online (Sandbox Code Playgroud)
Tho*_*mas 17
find,ls和perl针对40 000个文件进行了测试:速度相同(虽然我没有尝试清除缓存):
[user@server logs]$ time find . | wc -l
42917
real 0m0.054s
user 0m0.018s
sys 0m0.040s
[user@server logs]$ time /bin/ls -f | wc -l
42918
real 0m0.059s
user 0m0.027s
sys 0m0.037s
Run Code Online (Sandbox Code Playgroud)
并使用perl opendir/readdir,同时:
[user@server logs]$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"'
42918
real 0m0.057s
user 0m0.024s
sys 0m0.033s
Run Code Online (Sandbox Code Playgroud)
注意:我使用/ bin/ls -f确保绕过alias选项,这可能会减慢一点,-f以避免文件排序.没有-f的ls比find/perl慢两倍,除非ls与-f一起使用,它似乎是同一时间:
[user@server logs]$ time /bin/ls . | wc -l
42916
real 0m0.109s
user 0m0.070s
sys 0m0.044s
Run Code Online (Sandbox Code Playgroud)
我还想有一些脚本直接询问文件系统而没有所有不必要的信息.
根据Peter van der Heijden,glenn jackman和mark4o的回答进行测试.
托马斯
令我惊讶的是,一个简单的发现与 ls -f 非常相似
> time ls -f my_dir | wc -l
17626
real 0m0.015s
user 0m0.011s
sys 0m0.009s
Run Code Online (Sandbox Code Playgroud)
相对
> time find my_dir -maxdepth 1 | wc -l
17625
real 0m0.014s
user 0m0.008s
sys 0m0.010s
Run Code Online (Sandbox Code Playgroud)
当然,每次执行其中任何一个时,小数点后第三位的值都会移动一点,因此它们基本相同。但是请注意,它find返回一个额外的单位,因为它计算实际目录本身(并且,如前所述,ls -f返回两个额外的单位,因为它也计算 . 和 ..)。
ls花费更多时间对文件名进行排序。使用-f禁用的排序,这将节省一些时间:
ls -f | wc -l
Run Code Online (Sandbox Code Playgroud)
或者你可以使用find:
find . -type f | wc -l
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以根据您的要求更改输出,但这里是一个bash单行程序,我写的是递归计数并报告一系列数字命名目录中的文件数.
dir=/tmp/count_these/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$i => $(find ${dir}${i} -type f | wc -l),"; }
Run Code Online (Sandbox Code Playgroud)
这将递归查看给定目录中的所有文件(而不是目录),并以类似哈希的格式返回结果.对find命令的简单调整可以使你想要的文件类型更加具体,等等.
结果是这样的:
1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,
Run Code Online (Sandbox Code Playgroud)
我所知道的最快的 Linux 文件数是
locate -c -r '/home'
Run Code Online (Sandbox Code Playgroud)
有没有必要调用grep的!但如前所述,您应该拥有一个新的数据库(由 cron 作业每天更新,或由 手动更新sudo updatedb)。
从人定位
-c, --count
Instead of writing file names on standard output, write the number of matching
entries only.
Run Code Online (Sandbox Code Playgroud)
另外,您应该知道它也将目录计为文件!
顺便说一句:如果您想了解系统类型上的文件和目录
locate -S
Run Code Online (Sandbox Code Playgroud)
它输出目录、文件等的数量。
| 归档时间: |
|
| 查看次数: |
85364 次 |
| 最近记录: |