inotify只能监控单层目录变化,不能监控子目录中的变化情况。

本文详细介绍了如何使用inotify实现对子目录的监控,包括递归建立子目录的监控,解析inotify事件,以及具体处理函数。通过C++ STL map实现目录集合管理,便于追踪子目录的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

inotify只能监控单层目录变化,不能监控子目录中的变化情况。
如果需要监控子目录,需要在调用inotify_add_watch(int fd, char *dir, int mask):int建立监控时,递归建立子目录的监控,伪代码如下
void addwatch(int fd, char *dir, int mask)
{
wd = inotify_add_watch(fd, dir, mask);
向目录集合加入(wd, dir);
for (dir下所有的子目录subdir)
addwatch(fd, subdir, mask);
}
这样就可以得到一个目录集合,其中每一个wd对应一个子目录。
当你调用read获取信息时,可以得到一个下面的结构体
struct inotify_event
{
int wd; /* Watch descriptor. */
uint32_t mask; /* Watch mask. */
uint32_t cookie; /* Cookie to synchronize two events. */
uint32_t len; /* Length (including NULs) of name. */
char name __flexarr; /* Name. */
};
其中,通过event->wd和刚才记录的目录集合可以知道变动的具体子目录。
event->name为具体的文件名称。
event->name是一个char name[0]形式的桩指针,具体的name占据的长度可以由event->len得出

我的监控部分代码如下:
enum {EVENT_SIZE = sizeof(struct inotify_event)};
enum {BUF_SIZE = (EVENT_SIZE + 16) << 10};
void watch_mon(int fd)
{
int i, length;
void *buf;
struct inotify_event *event;
buf = malloc(BUF_SIZE);

while ((length = read(fd, buf, BUF_SIZE)) >= 0)
{
i = 0;
while (i < length)
{
event = buf + i;
if (event->len)
具体处理函数(event);
i += EVENT_SIZE + event->len;
}
}
close(fd);
exit(1);
}
在你的具体处理函数中,通过wd辨识子目录,通过name辨识文件

这是利用C++STLmap写的一个范例,可以监视当前目录下(含子目录)的变化,创建,删除过程(新建立的目录不能监视,只能通过监视到创建新目录的事件后重新初始化监视表)
新版1.1.0,可以监视创建的子目录,方法是,当do_action探测到新目录创建的动作时,调用inotify_add_watch追加新的监视
/*
Copyright (C) 2010-2011 LIU An (SuperHacker@china.com.cn)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <errno.h>
#include <dirent.h>

#include <map>
#include <string>
using namespace std;

void addwatch(int, char*, int);
static int filter_action(uint32_t mask);
int watch_init(int mask, char *root);
void addwatch(int fd, char *dir, int mask);
static void do_action(int fd, struct inotify_event *event);
void watch_mon(int fd);
static void send_mess(char *name, char *act, int ewd);
void append_dir(int fd, struct inotify_event *event, int mask);

map<int, string> dirset;

enum{MASK = IN_MODIFY | IN_CREATE | IN_DELETE};

int main(int argc, char **argv)
{
int fd;
if (argc != 2)
{
fprintf(stderr, "Usage: %s dir\n", argv[0]);
exit(1);
}

fd = watch_init(MASK, argv[1]);
watch_mon(fd);

return 0;
}

int watch_init(int mask, char *root)
{
int i, fd;

if ((fd = inotify_init()) < 0)
perror("inotify_init");
addwatch(fd, root, mask);
return fd;
}

void addwatch(int fd, char *dir, int mask)
{
int wd;
char subdir[512];
DIR *odir;
struct dirent *dent;

if ((odir = opendir(dir)) == NULL)
{
perror("fail to open root dir");
exit(1);
}
wd = inotify_add_watch(fd, dir, mask);
dirset.insert(make_pair(wd, string(dir)));

errno = 0;
while ((dent = readdir(odir)) != NULL)
{
if (strcmp(dent->d_name, ".") == 0
|| strcmp(dent->d_name, "..") == 0)
continue;
if (dent->d_type == DT_DIR)
{
sprintf(subdir, "%s/%s", dir, dent->d_name);
addwatch(fd, subdir, mask);
}
}

if (errno != 0)
{
perror("fail to read dir");
exit(1);
}

closedir (odir);
}

enum {EVENT_SIZE = sizeof(struct inotify_event)};
enum {BUF_SIZE = (EVENT_SIZE + 16) << 10};

void watch_mon(int fd)
{
int i, length;
void *buf;
struct inotify_event *event;
buf = malloc(BUF_SIZE);

while ((length = read(fd, buf, BUF_SIZE)) >= 0)
{
i = 0;
while (i < length)
{
event = (struct inotify_event*)(buf + i);
if (event->len)
do_action(fd, event);
i += EVENT_SIZE + event->len;
}
}
close(fd);
exit(1);
}

static char action[][10] =
{
"modified",
"accessed",
"created",
"removed"
};

enum{NEWDIR = IN_CREATE | IN_ISDIR};

static void do_action(int fd, struct inotify_event *event)
{
int ia, i;

if ((ia = filter_action(event->mask)) < 0)
return;

if ((event->mask & NEWDIR) == NEWDIR)
append_dir(fd, event, MASK);

send_mess(event->name, action[ia], event->wd);
}

void append_dir(int fd, struct inotify_event *event, int mask)
{
char ndir[512];
int wd;

sprintf(ndir, "%s/%s", dirset.find(event->wd)->second.c_str(),
event->name);
wd = inotify_add_watch(fd, ndir, mask);
dirset.insert(make_pair(wd, string(ndir)));
}

static int filter_action(uint32_t mask)
{
if (mask & IN_MODIFY)
return 0;
if (mask & IN_ACCESS)
return 1;
if (mask & IN_CREATE)
return 2;
if (mask & IN_DELETE)
return 3;
return -1;
}

static void send_mess(char *name, char *act, int ewd)
{
char format[] = "%s was %s.\n";
char file[512];

sprintf(file, "%s/%s", dirset.find(ewd)->second.c_str(), name);

printf(format, file, act);
}

参考资料是我们作业的提交,没有考虑递归创建子目录监控的问题。
参考资料:[url]http://59.67.33.217/homeworkref/sources/2010-2011-1.2008.software.os.inotify.tar.xz[/url]
转: [url]http://zhidao.baidu.com/question/218179319.com[/url]
<think>我们使用C++17的filesystem库来遍历目录并筛选文件。步骤:1.包含必要的头文件:<filesystem>和<iostream>,以及<string>和<vector>。2.使用std::filesystem::directory_iterator遍历目录。3.对于每个目录项,检查是否是普通文件(排除目录等)。4.检查文件扩展名是否符合要求(例如:.h,.cpp等)。5.将符合条件的文件路径存入一个容器(如vector)中。注意:需要编译器支持C++17,并在编译时启用C++17。示例代码:*/#include<iostream>#include<filesystem>#include<vector>#include<string>namespacefs=std::filesystem;std::vector<std::string>getFilesInDirectory(conststd::string&directoryPath,conststd::vector<std::string>&extensions){std::vector<std::string>fileList;try{for(constauto&entry:fs::directory_iterator(directoryPath)){if(entry.is_regular_file()){//获取文件扩展名std::stringext=entry.path().extension().string();//将扩展名转换为小写,以便不区分大小写(可选)//注意:这里为了简单,我们直接比较,如果需要不区分大小写,可以统一转成小写或大写for(constauto&allowedExt:extensions){if(ext==allowedExt){fileList.push_back(entry.path().string());break;//找到一个匹配就跳出内层循环}}}}}catch(constfs::filesystem_error&e){std::cerr<<"文件系统错误:"<<e.what()<<'\n';}returnfileList;}intmain(){std::stringpath=".";//当前目录std::vector<std::string>extensions={".h",".cpp"};//扩展名列表std::vector<std::string>files=getFilesInDirectory(path,extensions);std::cout<<"符合条件的文件列表:"<<std::endl;for(constauto&file:files){std::cout<<file<<std::endl;}return0;}/*编译说明(以g++为例):g++-std=c++17-olist_fileslist_files.cpp注意:如果使用MinGW,请确保版本足够新(支持C++17的filesystem)。另外,在链接时可能需要添加-lstdc++fs(对于某些旧版本gcc),但较新版本已经不需要。如果需要不区分扩展名大小写,可以在比较前将扩展名转换为小写(或大写)。例如:将`ext=entry.path().extension().string();`替换为:std::stringext=entry.path().extension().string();std::transform(ext.begin(),ext.end(),ext.begin(),::tolower);同时,也要将允许的扩展名列表中的字符串转换为小写(或者在比较时临时转换)。另外,如果需要在子目录中搜索,可以使用递归目录迭代器(recursive_directory_iterator)。问题:如何递归地搜索子目录?我们可以修改函数,使用递归迭代器,并检查是否是普通文件。递归版本函数:*/std::vector<std::string>getFilesInDirectoryRecursive(conststd::string&directoryPath,conststd::vector<std::string>&extensions){std::vector<std::string>fileList;try{for(constauto&entry:fs::recursive_directory_iterator(directoryPath)){if(entry.is_regular_file()){std::stringext=entry.path().extension().string();for(constauto&allowedExt:extensions){if(ext==allowedExt){fileList.push_back(entry.path().string());break;}}}}}catch(constfs::filesystem_error&e){std::cerr<<"文件系统错误:"<<e.what()<<'\n';}returnfileList;}/*注意事项:1.递归遍历可能会遇到权限问题,因此使用try-catch捕获异常。2.递归遍历可能会遍历到符号链接,如果不想跟随符号链接,可以在迭代器构造时设置选项:fs::recursive_directory_iterator(directoryPath,fs::directory_options::skip_permission_denied)等。另外,如果扩展名列表较长,可以考虑使用一个set来存储,以提高查找效率。优化:使用set存储扩展名(因为set的查找效率为O(logn))例如:#include<set>std::set<std::string>extSet(extensions.begin(),extensions.end());if(extSet.find(ext)!=extSet.end()){...}但是注意,我们的extensions列表很小,所以使用vector和循环也是可以的。如果用户需要搜索多个目录,可以修改函数参数,接受一个目录列表。如果用户需要更复杂的文件名匹配(如通配符),则可能需要使用正则表达式,但扩展名匹配通常用集合就够了。以上代码在支持C++17的编译器上运行。如果使用C语言,可以使用dirent.h(跨平台)或WindowsAPI(仅Windows)等,但这里我们优先使用标准C++17的方法。对于C语言(跨平台):我们可以使用dirent.h(POSIX兼容,Windows也有dirent的兼容实现)。下面是一个示例:注意:C语言版本需要自己处理递归和扩展名匹配。由于问题要求C或C++,这里再提供一个C语言版本(使用dirent.h)的示例(非递归,仅单层目录):注意:这个版本在Windows和Linux都可以使用,但需要确保有dirent.h(Windows上可以下载一个dirent.h放在项目中)。下面是一个简单的C语言版本:*/#ifdef_WIN32#include<direct.h>#definegetcwd_getcwd#else#include<unistd.h>#endif#include<stdio.h>#include<string.h>#include<dirent.h>#include<sys/stat.h>voidlistFiles(constchar*dirPath,constchar**extensions,intextCount){DIR*dir;structdirent*entry;structstatstatbuf;if((dir=opendir(dirPath))==NULL){perror("无法打开目录");return;}//更改当前工作目录到目标目录,以便使用相对路径访问文件chdir(dirPath);while((entry=readdir(dir))!=NULL){if(strcmp(entry->d_name,".")==0||strcmp(entry->d_name,"..")==0)continue;//获取文件信息if(stat(entry->d_name,&statbuf)==-1){perror("stat错误");continue;}if(S_ISREG(statbuf.st_mode)){//是普通文件//检查扩展名char*dot=strrchr(entry->d_name,'.');if(dot){intmatch=0;for(inti=0;i<extCount;i++){if(strcmp(dot,extensions[i])==0){match=1;break;}}if(match){//这里打印文件路径(相对于原始dirPath)printf("%s/%s\n",dirPath,entry->d_name);}}}}closedir(dir);}/*注意:这个C语言版本没有递归遍历子目录,并且更改了当前工作目录(这可能会影响程序的其它部分)。为了避免更改工作目录,我们可以拼接路径,但要注意路径分隔符(Windows用'\',Linux用'/')。改进的C语言版本(不改变工作目录):*/#include<stdlib.h>//formalloc,freevoidlistFilesWithoutChdir(constchar*dirPath,constchar**extensions,intextCount){DIR*dir;structdirent*entry;structstatstatbuf;charfullpath[PATH_MAX];//需要包含<limits.h>,或者使用动态分配if((dir=opendir(dirPath))==NULL){perror("无法打开目录");return;}while((entry=readdir(dir))!=NULL){if(strcmp(entry->d_name,".")==0||strcmp(entry->d_name,"..")==0)continue;//构建完整路径snprintf(fullpath,sizeof(fullpath),"%s/%s",dirPath,entry->d_name);if(stat(fullpath,&statbuf)==-1){perror("stat错误");continue;}if(S_ISREG(statbuf.st_mode)){char*dot=strrchr(entry->d_name,'.');if(dot){intmatch=0;for(inti=0;i<extCount;i++){if(strcmp(dot,extensions[i])==0){match=1;break;}}if(match){printf("%s\n",fullpath);}}}}closedir(dir);}/*注意:上面的代码中,PATH_MAX可能在某些系统上未定义,或者路径过长。为了避免固定大小的缓冲区,可以使用动态分配,但为了简单,这里使用了固定大小。如果需要递归遍历,则需要递归地进入子目录(同样需要判断是否是目录,然后递归调用)。由于C语言版本较为繁琐,推荐使用C++17的filesystem。总结:根据用户需求,如果使用C++且编译器支持C++17,推荐使用filesystem库;如果使用C语言,可以使用dirent.h进行目录遍历。相关问题:1.在C++中如何递归遍历目录下的所有文件(包括子目录)?2.如何在不区分大小写的情况下匹配文件扩展名?3.在C语言中,如何跨平台(Windows/Linux)实现目录遍历?4.使用C++17的filesystem库时,如何处理可能出现的异常?5.如何提高文件扩展名匹配的效率(当扩展名列表很长时)?</think>### C/C++ 获取目录下符合特定格式的文件列表 以下是使用 **C++17** 标准实现的方法(推荐),以及兼容旧标准的 **C++** 和 **C** 语言方案: --- #### ✅ 推荐方案:C++17 `<filesystem>` 库(跨平台) ```cpp #include <iostream> #include <filesystem> #include <vector> #include <string> namespace fs = std::filesystem; int main() { std::string targetDir = "./"; // 目标目录(可修改) std::vector<std::string> fileList; try { // 遍历目录并筛选文件 for (const auto& entry : fs::directory_iterator(targetDir)) { if (entry.is_regular_file()) { std::string ext = entry.path().extension().string(); // 筛选扩展名(示例:.h/.cpp文件) if (ext == ".h" || ext == ".cpp") { fileList.push_back(entry.path().filename().string()); } } } // 输出结果 std::cout << "匹配的文件列表:" << std::endl; for (const auto& fname : fileList) { std::cout << "• " << fname << std::endl; } } catch (const fs::filesystem_error& e) { std::cerr << "错误: " << e.what() << std::endl; } return 0; } ``` **特点**: - **跨平台**:Windows/Linux/macOS 通用 - **扩展名修改**:调整 `if (ext == ".h" || ...)` 中的条件 - **编译命令**:`g++ -std=c++17 -o file_finder file_finder.cpp` --- #### ⚙️ 旧版 C++ 方案(Windows API) ```cpp #include <windows.h> #include <iostream> #include <vector> std::vector<std::string> findFiles(const std::string& dir, const std::string& pattern) { std::vector<std::string> files; WIN32_FIND_DATA findData; HANDLE hFind = FindFirstFile((dir + "\\" + pattern).c_str(), &findData); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { files.push_back(findData.cFileName); } } while (FindNextFile(hFind, &findData)); FindClose(hFind); } return files; } int main() { auto files = findFiles("C:\\MyFolder", "*.txt"); // 示例:所有.txt文件 for (const auto& f : files) std::cout << f << std::endl; return 0; } ``` --- #### ⚙️ C 语言方案(POSIX 标准) ```c #include <dirent.h> #include <stdio.h> #include <string.h> #include <regex.h> // 正则表达式支持 void find_files(const char* dir, const char* pattern) { DIR *dp = opendir(dir); struct dirent *ep; regex_t regex; if (regcomp(&regex, pattern, REG_EXTENDED)) return; // 编译正则表达式 if (dp != NULL) { while ((ep = readdir(dp))) { if (ep->d_type == DT_REG) { // 仅普通文件 if (!regexec(&regex, ep->d_name, 0, NULL, 0)) { printf("匹配文件: %s\n", ep->d_name); } } } closedir(dp); regfree(&regex); } } int main() { find_files("/home/user/docs", ".*\\.(pdf|docx)$"); // 示例:PDF/DOCX文件 return 0; } ``` --- ### 🔍 关键扩展方法 1. **递归子目录** C++17 使用 `recursive_directory_iterator` C 语言通过 `ep->d_type == DT_DIR` 判断目录后递归调用 2. **自定义过滤规则** - **通配符**:`*.log`(Windows API 原生支持) - **正则表达式**:如 C 语言示例中的 `".*\\.(pdf|docx)$"` - **文件名前缀/后缀**:`strstr(ep->d_name, "temp_") != NULL` 3. **性能优化** - 大目录处理:使用异步 I/O 或内存映射 - 并行遍历:C++17 结合 `<thread>` 库 --- ### 📚 相关问题 1. 如何在遍历目录时处理符号链接和隐藏文件? 2. 当目标目录包含数百万文件时,如何优化搜索性能? 3. 如何跨平台实现文件修改时间的过滤(如最近7天)? 4. 在 C++ 中如何将文件搜索功能封装成可重用的类? 5. Linux 系统下如何利用 `inotify` 监控目录实时变化?[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值