You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:02:32