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

如何通过inotify获取被监控文件的新增数据(避免全量读取)

问题

我正在用inotify监控文件内容变化,希望只获取新增的内容,但不知道怎么计算文件中新增的字节数。能不能通过inotify实现?想要一种替代方案,避免读取整个文件、和内存中之前的文件内容对比生成差异的方式,因为文件会随时间变大。

附上的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <fcntl.h>

void watchFile(char* filePath) {
    // Create an inotify instance
    int fd = inotify_init();
    if (fd == -1) {
        perror("inotify_init");
        return;
    }

    // Add the file to the watch list
    int wd = inotify_add_watch(fd, filePath, IN_MODIFY);
    if (wd == -1) {
        perror("inotify_add_watch");
        return;
    }

    // Set up a buffer to hold incoming events
    char buffer[4096];

    while (true) {
        // Wait for events
        int length = read(fd, buffer, sizeof(buffer));
        if (length == -1) {
            perror("read");
            return;
        }

        // Parse the events
        int offset = 0;
        while (offset < length) {
            struct inotify_event* event = (struct inotify_event*) &buffer[offset];
            if (event->len) {
                // Seek to the end of the file
                int file = open(filePath, O_RDONLY);
                lseek(file, 0, SEEK_END);

                // Read the new data from the file
                char newData[???] = {0}; // how do i calculate this?
                read(file, &newData[0], ??);

                // Handle the new data
                printf("New data: %s\n", newData);

                close(file);
            }
            offset += sizeof(struct inotify_event) + event->len;
        }
    }

    // Stop watching the file
    inotify_rm_watch(fd, wd);

    // Close the inotify instance
    close(fd);
}

int main() {
    watchFile("path/to/file.txt");
    return 0;
}

期望效果:编译后的程序./a.out,当向path/to/file.txt写入baa、foo、hehe等内容时,输出:

New data: baa
New data: foo
New data: hehe

解决方案

inotify本身不会直接返回新增内容的字节数,但可以通过记录文件读取偏移量的方式实现只读取新增部分,完全不需要读取整个文件或对比旧内容。

核心思路

  1. 程序启动时获取文件初始大小,作为后续读取的起始偏移量
  2. 每次收到IN_MODIFY事件时,计算当前文件大小与上次偏移量的差值,这个差值就是新增内容的长度
  3. 从上次偏移量位置开始读取对应长度的内容,读取完成后更新偏移量为当前文件大小
  4. 额外监控IN_TRUNCATE事件,处理文件被截断(比如清空)的情况,此时重置偏移量为0

修改后的代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

void watchFile(char* filePath) {
    int fd = inotify_init();
    if (fd == -1) {
        perror("inotify_init");
        return;
    }

    // 同时监控文件修改和截断事件,处理文件被清空的场景
    int wd = inotify_add_watch(fd, filePath, IN_MODIFY | IN_TRUNCATE);
    if (wd == -1) {
        perror("inotify_add_watch");
        close(fd);
        return;
    }

    // 获取文件初始大小,作为读取起始偏移量
    off_t current_offset = 0;
    struct stat file_stat;
    if (stat(filePath, &file_stat) == 0) {
        current_offset = file_stat.st_size;
    }

    char event_buffer[4096];
    char read_buffer[1024]; // 可根据实际需求调整缓冲区大小

    while (true) {
        int length = read(fd, event_buffer, sizeof(event_buffer));
        if (length == -1) {
            perror("read");
            break;
        }

        int offset = 0;
        while (offset < length) {
            struct inotify_event* event = (struct inotify_event*) &event_buffer[offset];

            // 处理文件截断事件,重置偏移量到文件开头
            if (event->mask & IN_TRUNCATE) {
                current_offset = 0;
            }
            // 处理文件修改事件,读取新增内容
            else if (event->mask & IN_MODIFY) {
                int file_fd = open(filePath, O_RDONLY);
                if (file_fd == -1) {
                    perror("open");
                    offset += sizeof(struct inotify_event) + event->len;
                    continue;
                }

                // 获取当前文件大小
                if (fstat(file_fd, &file_stat) == -1) {
                    perror("fstat");
                    close(file_fd);
                    offset += sizeof(struct inotify_event) + event->len;
                    continue;
                }

                off_t file_size = file_stat.st_size;
                if (file_size > current_offset) {
                    // 移动到上次读取结束的位置
                    lseek(file_fd, current_offset, SEEK_SET);

                    // 循环读取新增内容,避免缓冲区不足
                    ssize_t bytes_read;
                    while ((bytes_read = read(file_fd, read_buffer, sizeof(read_buffer) - 1)) > 0) {
                        read_buffer[bytes_read] = '\0';
                        printf("New data: %s", read_buffer);
                        current_offset += bytes_read;
                    }
                } else if (file_size < current_offset) {
                    // 处理未触发IN_TRUNCATE的截断场景,重置偏移量
                    current_offset = file_size;
                }

                close(file_fd);
            }

            offset += sizeof(struct inotify_event) + event->len;
        }
    }

    inotify_rm_watch(fd, wd);
    close(fd);
}

int main() {
    watchFile("path/to/file.txt");
    return 0;
}

代码关键点说明

  • 偏移量维护:通过current_offset记录上次读取到的文件位置,确保每次只读取新增部分
  • 事件扩展:新增IN_TRUNCATE监控,处理文件被清空的场景,避免后续读取错误
  • 分段读取:用循环读取适配大段新增内容,防止缓冲区不足导致内容丢失
  • 性能优化:全程只读取新增内容,无需加载整个文件,完美适配大文件场景

内容的提问来源于stack exchange,提问作者Coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:05:10