如何利用inotify检测编辑器修改的真实文件名?
问题描述
为了识别Texstudio、Geany等编辑器修改的特定文件名,我用inotify实现了文件监控程序,代码如下:
#include<stdio.h> #include<sys/inotify.h> #include<unistd.h> #include<stdlib.h> #include<signal.h> #include<fcntl.h> // library for fcntl function //inotify_02.c #define MAX_EVENTS 1024 /* Maximum number of events to process*/ #define LEN_NAME 1024 /* Assuming that the length of the filename won't exceed 16 bytes*/ #define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/ #define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*buffer to store the data of events*/ int file_descriptor,watch_descriptor; void sig_handler(int sig){ /* Step 5. Remove the watch descriptor and close the inotify instance*/ inotify_rm_watch( file_descriptor, watch_descriptor ); close( file_descriptor ); exit( 0 ); } int main(int argc, char **argv){ char *path_to_be_watched; signal(SIGINT,sig_handler); path_to_be_watched = argv[1]; /* Step 1. Initialize inotify */ file_descriptor = inotify_init(); if (fcntl(file_descriptor, F_SETFL, O_NONBLOCK) < 0) // error checking for fcntl exit(2); /* Step 2. Add Watch */ watch_descriptor = inotify_add_watch(file_descriptor,path_to_be_watched,IN_MODIFY | IN_CREATE | IN_DELETE); if(watch_descriptor==-1) printf("Could not watch : %s\n",path_to_be_watched); else printf("Watching : %s\n",path_to_be_watched); while(1) { int i=0,length; char buffer[BUF_LEN]; /* Step 3. Read buffer*/ length = read(file_descriptor,buffer,BUF_LEN); /* Step 4. Process the events which has occurred */ while(i<length) { struct inotify_event *event = (struct inotify_event *) &buffer[i]; if(event->len) { if ( event->mask & IN_CLOSE ) /* IN_CLOSE Equates to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE * File or directory not opened for writing was closed. or * File opened for writing was closed. */ { if ( event->mask & IN_ISDIR ) { printf("File or directory %s not opened for writing was closed.\n", event->name ); } else { printf("File %sopened for writing was closed.\n", event->name ); } } /*-------*/ else if ( event->mask & IN_CREATE ) { if ( event->mask & IN_ISDIR ) { printf("The directory %s was created.\n", event->name ); } else { printf("The file %s was created.\n", event->name ); } } /*-------*/ else if ( event->mask & IN_DELETE ) { if ( event->mask & IN_ISDIR ) { printf("The directory %s was deleted.\n", event->name ); } else { printf("The file %s was deleted.\n", event->name ); } } /*-------*/ else if ( event->mask & IN_MODIFY ) { if ( event->mask & IN_ISDIR ) { printf("The directory %s was modified.\n", event->name ); } else { printf("The file %s was modified.\n", event->name ); } } } i += EVENT_SIZE + event->len; } } }
测试中遇到两个问题:
- 使用编辑器修改
inotify_02.c时,程序仅输出临时文件(如.goutputstream-V0EIG2)的创建、修改事件,无法获取真实文件名; - 复制文件时能正常获取新文件的真实名称,但LaTeX编译时会产生大量无关事件,程序需仅识别特定文件的修改。
已知多数编辑器并非原地编辑文件,而是采用先删除再重写的overwrite方式,IN_MODIFY事件无效,应监控IN_CLOSE_WRITE/IN_MOVED_FROM事件以获取文件稳定状态,但仍无法获取真实文件名。请问是否有方法获取编辑器修改的真实文件名?
解决方案
1. 追踪临时文件与目标文件的关联(原子替换型编辑器)
Geany、Texstudio这类编辑器普遍采用临时文件写入+原子重命名的保存逻辑:先将内容写入临时文件,再把临时文件重命名为原文件名。可以通过以下方式关联真实文件:
- 调整监控事件掩码,添加
IN_MOVED_FROM | IN_MOVED_TO; - 捕获到临时文件(比如
.goutputstream-*、filename~这类有固定命名特征的文件)的IN_MOVED_TO事件时,检查目标文件名是否属于你关注的类型(如.c、.tex后缀); - 结合事件序列判断:临时文件的
IN_CLOSE_WRITE事件之后,若出现原文件的IN_DELETE+新文件的IN_CREATE,则说明原文件被替换。
2. 监控目标文件的inode变化
文件被原子替换后,inode会发生改变。可以通过inode追踪真实文件的修改:
- 初始化时用
stat()获取目标文件的inode并记录; - 捕获到
IN_CLOSE_WRITE或IN_CREATE事件时,检查目标文件的inode是否与记录值不一致,若不一致则说明文件被修改; - 配合
IN_DELETE_SELF事件:当原文件被删除时,后续的IN_CREATE+IN_CLOSE_WRITE通常对应新文件替换原文件的操作,此时需更新inode记录并重新添加监控。
3. 过滤无关事件,聚焦特定文件
针对LaTeX编译产生的大量无效事件,可做以下过滤:
- 只处理后缀匹配的文件(如
.tex、.c),在事件处理逻辑中判断event->name的后缀,不符合则直接跳过; - 排除编译临时文件(如
.aux、.log、.out等),直接过滤这些文件名; - 若仅关注单个文件,直接监控该文件而非整个目录,注意文件被替换后需重新添加监控。
4. 优化inotify监控参数
修改代码中的事件掩码,替换为更精准的组合:
watch_descriptor = inotify_add_watch(file_descriptor, path_to_be_watched, IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE_SELF);
该组合能捕获文件写入完成、重命名、原文件被删除的关键事件,减少无效事件干扰。
示例代码片段(临时文件关联)
在事件处理逻辑中添加重命名事件的处理:
else if (event->mask & IN_MOVED_TO) { if (!(event->mask & IN_ISDIR)) { // 匹配编辑器临时文件特征 if (strstr(event->name, ".goutputstream-") != NULL) { // 检查目标文件的修改状态 struct stat st; if (stat("inotify_02.c", &st) == 0) { printf("Real file inotify_02.c was modified via temp file %s\n", event->name); } } } }
内容的提问来源于stack exchange,提问作者Manmax
相关产品推荐
相关产品推荐

