c bash 代码遍历文件夹下所有文件
2016-05-10 16:12
686 查看
用C代码、bash实现代码遍历文件夹下所有文件
递归方式阅读比较清晰,会因使用栈的空间来保存局部变量(还有参数、返回地址等)而导致的stack overflow的问题。
系统给程序分配的内存有一部分是用来作栈使用的,栈在最大的地址开始,需要“申请”栈的时候就让栈顶指针也就是esp指向更低(往下“走”)的空间,当栈增长太大乃至超过堆(堆是往上“走”的)的范围时就是所谓stack overflow/collide,可以想象的是要么栈破坏堆上存储的数据,要么就是程序“返回”到非法的地址去执行指令,多么可怕啊,不过现在堆栈貌似是被分配在不同内存页上的,操作系统尽了最大的努力对堆栈进行保护,所以其实也不是很恐怖,大不了就是整个进程被操作系统kill掉。
尽管如此,谁也不希望自己的程序这么死掉,那多不给力啊!stack overflow的异常即使用try/catch也不行(C++不能catch这种异常,必须用Windows自己的)递归需要额外的函数调用开销,如果代码是在多线程环境下执行,那还会面临一个系统分配给每个线程的堆栈大小限制的问题,Windows下每个线程默认分配1M的空间。
那看看上面那个递归版本的函数会需要多少局部空间:WIN32_FIND_DATA 的大小是 320,MAX_PATH的buffer是260,其余变量和参数忽略,那么一次函数调用需要580个字节(ANSI环境),也就是说最大能递归多少层?
答案是 1808 层。
换句话说,从根目录开始,最多只能遍历到1907层深的文件夹结构,再深层的文件就遍历不了了。而实际上,我们是没办法创建这么深层次的目录树结构的,试试看就知道Windows会提示超出限制。
其实看看 MAX_PATH 的值就知道了,不是才260么,哪有可能给你弄到1800多层?
NTFS不是据称很先进么,莫非也这么不给力?在多字节字符环境下,这个限制将使得我们最多只能创建一百多层深的文件夹结构。
翻翻MSDN上关于FindFirstFile的说明,原来微软还留有一手:
In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 widecharacters, call the Unicode version of the function and prepend "\\?\" to the path. For more information, see Naming a File.
简单说为了让这个限制突破到32767个宽字节字符(说了是宽字符了,那当然得是UNICODE环境下了),就要在路径前加上 \\?\ (这个办法有个缺点,那就是不能访问根目录)。
这下,我们完全有机会遇到1M的线程堆栈限制,虽然搞不懂为什么既然微软已经考虑到并提供了增加文件路径长度的方案,而我们仍然不能创建那么长的路径,但这至少给写个非递归版本的遍历文件函数提供了个理由。
引用 http://blog.csdn.net/yufei_email/article/details/42624551 代码
2. http://www.codeproject.com/KB/cpp/recursedir.aspx
3. http://www.codeproject.com/KB/edit/XEditPrompt.aspx
递归方式实现如下:
void listdir(char *path) { DIR *ptr_dir; struct dirent *dir_entry; int i = 0; char *child_path; char *file_path; struct stat sb_sub = {0}; struct stat sb = {0}; if (0 != stat(ppath, &sb)) return; child_path = (char*)malloc(sizeof(char)*MAX_PATH_LENGTH); if(child_path == NULL) { printf("allocate memory for path failed.\n"); return; } memset(child_path, 0, sizeof(char)*MAX_PATH_LENGTH); file_path = (char*)malloc(sizeof(char)*MAX_PATH_LENGTH); if(file_path == NULL) { printf("allocate memory for file path failed.\n"); free(child_path); child_path = NULL; return; } memset(file_path, 0, sizeof(char)*MAX_PATH_LENGTH); ptr_dir = opendir(path); while((dir_entry = readdir(ptr_dir)) != NULL) { if(strcmp(dir_entry->d_name,".") == 0 || strcmp(dir_entry->d_name,"..") == 0) continue; memset(&sb_sub, 0, sizeof(struct stat)); sprintf(child_path, "%s/%s", path, dir_entry->d_name); if(0 != stat(child_path, &sb_sub)) continue; visit_dirs++; if(S_ISDIR(sb_sub.st_mode)) { printf("[DIR]%s\n", child_path); listdir(child_path); } else { sprintf(file_path, "%s/%s", path, dir_entry->d_name); printf("[FILE]%s\n", file_path); visit_files++; } } free(child_path); child_path = NULL; free(file_path); file_path = NULL; }
递归方式阅读比较清晰,会因使用栈的空间来保存局部变量(还有参数、返回地址等)而导致的stack overflow的问题。
系统给程序分配的内存有一部分是用来作栈使用的,栈在最大的地址开始,需要“申请”栈的时候就让栈顶指针也就是esp指向更低(往下“走”)的空间,当栈增长太大乃至超过堆(堆是往上“走”的)的范围时就是所谓stack overflow/collide,可以想象的是要么栈破坏堆上存储的数据,要么就是程序“返回”到非法的地址去执行指令,多么可怕啊,不过现在堆栈貌似是被分配在不同内存页上的,操作系统尽了最大的努力对堆栈进行保护,所以其实也不是很恐怖,大不了就是整个进程被操作系统kill掉。
尽管如此,谁也不希望自己的程序这么死掉,那多不给力啊!stack overflow的异常即使用try/catch也不行(C++不能catch这种异常,必须用Windows自己的)递归需要额外的函数调用开销,如果代码是在多线程环境下执行,那还会面临一个系统分配给每个线程的堆栈大小限制的问题,Windows下每个线程默认分配1M的空间。
那看看上面那个递归版本的函数会需要多少局部空间:WIN32_FIND_DATA 的大小是 320,MAX_PATH的buffer是260,其余变量和参数忽略,那么一次函数调用需要580个字节(ANSI环境),也就是说最大能递归多少层?
答案是 1808 层。
换句话说,从根目录开始,最多只能遍历到1907层深的文件夹结构,再深层的文件就遍历不了了。而实际上,我们是没办法创建这么深层次的目录树结构的,试试看就知道Windows会提示超出限制。
其实看看 MAX_PATH 的值就知道了,不是才260么,哪有可能给你弄到1800多层?
NTFS不是据称很先进么,莫非也这么不给力?在多字节字符环境下,这个限制将使得我们最多只能创建一百多层深的文件夹结构。
翻翻MSDN上关于FindFirstFile的说明,原来微软还留有一手:
In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 widecharacters, call the Unicode version of the function and prepend "\\?\" to the path. For more information, see Naming a File.
简单说为了让这个限制突破到32767个宽字节字符(说了是宽字符了,那当然得是UNICODE环境下了),就要在路径前加上 \\?\ (这个办法有个缺点,那就是不能访问根目录)。
这下,我们完全有机会遇到1M的线程堆栈限制,虽然搞不懂为什么既然微软已经考虑到并提供了增加文件路径长度的方案,而我们仍然不能创建那么长的路径,但这至少给写个非递归版本的遍历文件函数提供了个理由。
非递归方式实现
思路:使用一个stack,遍历文件夹时,如果是dir则push该dir到stack,如果是文件则做文件处理,循环pop出stack中的dir 并处理。引用 http://blog.csdn.net/yufei_email/article/details/42624551 代码
#include <unistd.h> #include <cstdio> #include <dirent.h> #include <sys/stat.h> #include <sys/time.h> #include <iostream> #include <deque> #include <list> #include <pthread.h> using namespace std; bool g_bIsListDirEnd = false; pthread_mutex_t g_mutex; void FormatDir(string& strDir) { if (strDir.at(strDir.length() -1) != '\\' && strDir.at(strDir.length() -1) != '/') { strDir += '/'; } } void* thrd_func(void *arg) { list<string>* pList = (list<string>*)arg; if (!arg) { cout << "arg is null" << endl; pthread_exit((void *)1); } int nCount = 0; while(true) { string strFileName; pthread_mutex_lock(&g_mutex); if (pList->size() > 0) { strFileName = pList->front(); pList->pop_front(); ++nCount; } pthread_mutex_unlock(&g_mutex); if (!strFileName.empty()) { usleep(10000); if (nCount % 1000 == 0) { cout << strFileName << " " << nCount << endl; } } else { usleep(1); } pthread_mutex_lock(&g_mutex); int nSize = pList->size(); pthread_mutex_unlock(&g_mutex); if (g_bIsListDirEnd && nSize == 0) { break; } } cout << "thread deals total files:" << nCount << endl; } void ListDir(const string& strTopDir) { deque<string> deqDirs; deqDirs.push_back(strTopDir); list<string> listFiles; const int nThreadNum = 4; pthread_t tid[nThreadNum]; for(int i = 0; i < nThreadNum; ++i) { if (pthread_create(&tid[i], NULL, thrd_func, &listFiles) != 0) { cout << "create thread failed:" << i << endl; return; } } int nCountDirs = 0; int nCountFiles = 0; while(deqDirs.size() > 0) { string strDir = deqDirs.front(); deqDirs.pop_front(); if (strDir.empty()) { continue; } DIR *dir; if(!(dir = opendir(strDir.c_str()))) { cout << "open dir failed:" << strDir << endl; continue; } FormatDir(strDir); struct dirent *file; while((file = readdir(dir)) != NULL) { if (file->d_name[0] == '.') { continue; } string strFullPath = strDir + file->d_name; struct stat stFile; if(stat(strFullPath.c_str(), &stFile) >= 0 && S_ISDIR(stFile.st_mode)) { if (strFullPath.find("/proc") == string::npos && strFullPath.find("/sys") == string::npos && strFullPath.find("/dev") == string::npos) { deqDirs.push_back(strFullPath); ++nCountDirs; } } else { while(true) { pthread_mutex_lock(&g_mutex); int nSize = listFiles.size(); pthread_mutex_unlock(&g_mutex); if (nSize < 5000) { break; } cout << "wait files dealed" << endl; usleep(1000000); } pthread_mutex_lock(&g_mutex); listFiles.push_back(strFullPath); pthread_mutex_unlock(&g_mutex); ++nCountFiles; } } closedir(dir); } cout << "list dir end" << endl; g_bIsListDirEnd = true; for(int i = 0; i < nThreadNum; ++i) { void *tret; pthread_join(tid[i],&tret); } cout << "dirs:" << nCountDirs << " files:" << nCountFiles << " total:" << nCountDirs + nCountFiles << endl; } int main(int argc, char* argv[]) { if (argc != 2) { printf("argc error!\n"); return -1; } pthread_mutex_init(&g_mutex,NULL); struct timeval tv1; gettimeofday(&tv1, NULL); ListDir(argv[1]); struct timeval tv2; gettimeofday(&tv2, NULL); cout << "cost time:" << (tv2.tv_sec - tv1.tv_sec)*1000 + (tv2.tv_usec - tv1.tv_usec)/1000 << "ms" << endl; pthread_mutex_destroy(&g_mutex); return 0; }
参考
1. http://www.codeproject.com/KB/files/CEnum_enumeration.aspx2. http://www.codeproject.com/KB/cpp/recursedir.aspx
3. http://www.codeproject.com/KB/edit/XEditPrompt.aspx
相关文章推荐
- shell变量
- shell if语句
- Xshell 连接 Ubuntu虚拟机
- Bash循环与分支浅谈
- 用PHP执行shell命令
- Shell脚本 curl获取必应每日壁纸(Mac OS)
- Shell之实现多线程
- 常用shell命令
- shell 生成数组
- shell教程-002:常见的Shell种类
- shell教程-001:shell简介 什么是shell,shell命令的两种执行方式
- Bash 的 shopt extglob ( 用来删除指定文件以外的文件,反向删除)
- PowerShell学习的一小步,工作的一大步
- Shell脚本(自动填充函数模板)
- bash中单引号、双引号、反单引号等的区别
- Linux Shell 使用技巧
- Linux bash 扩展
- shell(一)
- shell爬虫
- 文章标题