Как надежно поддерживать наблюдение за отредактированными файлами с помощью inotify?

Как надежно поддерживать наблюдение за отредактированными файлами с помощью inotify?

Я хотел бы отслеживать файл с помощью inotifyи запускать какой-то код, когда кто-то меняет содержимое ( IN_MODIFYили IN_CLOSE_WRITE), но я сталкиваюсь с проблемами, когда inotifyперестает возвращать события, когда пользователи редактируют файл с помощью своего любимого инструмента. Файл должен быть простым (одна строка, без пробелов, максимум 20 символов). Я бы предпочел не ограничивать их использование, но я не уверен, как обрабатывать различные ситуации.

Я использую inotify, и вот события, которые я получаю, когда различные приложения редактируют файл:

Действие События inotify
touch file IN_OPEN
echo "data" > file IN_MODIFY, IN_OPEN, IN_ACCESS, затемIN_CLOSE_NOWRITE
nano file(при открытии) IN_OPEN
nano file(на ^O) IN_MODIFY, IN_CLOSE_WRITE, IN_OPEN,IN_ACCESS
vim file(при открытии) IN_OPEN,IN_CLOSE_NOWRITE
vim file(на :w) IN_MOVE_SELF, IN_ATTRIB, то события перестают поступать из этого файла
gedit file(при открытии) IN_OPEN, IN_CLOSE_NOWRITE,IN_ACCESS
gedit file(при сохранении) IN_OPEN, IN_CLOSE_WRITE, IN_ATTRIB, то события перестают поступать из этого файла
mv newfile file IN_ATTRIB, то события из этого файла перестают поступать

В какой-то момент мне показалось, что я увидел geditтриггер, а IN_DELETE_SELFзатем он затих.

В случае, когда пользователь использует vimи gedit, я перестаю получать inotifyсобытия после того, как пользователь закончил редактирование. Как мне с этим бороться?

Единственное, что я вижу общего, это IN_ATTRIBсобытие. Я подозреваю, что когда я получаю событие IN_ATTRIB, я должен inotify_rm_watch()это wd, а затем заново создать новое inotify_add_watch()на основе того же пути. Но правильный ли это подход?

Другим вариантом может быть просмотр родительского каталога. Имя затронутого файла включено в inotify_event::name, и поэтому я мог бы отфильтровать интересующий меня файл и запустить любой IN_MODIFYили IN_CLOSE_WRITEтам, где nameсовпадает с моим интересующим меня файлом.

решение1

Как упоминает ikkachu, некоторые редакторы создают новый файл, затем заменяют оригинальный, изменяя inode. Это означает, что любые часы на исходном дескрипторе часов истекут.

Ответ — посмотреть родительский каталог и проверить изменения в любом файле с целевым именем. Что-то вроде этого:

namespace fs = std::filesystem;
fs::path path = "./file1";
assert( !path.is_directory() );

int fd = inotify_init();

int wd = inotify_add_watch(
    fd, 
    path.parent_path().c_str(),
    IN_MODIFY | IN_CREATE | IN_CLOSE_WRITE
);  

...

inotify_event event;
read(fd, &event, BUF_SIZE);

if (wd == event->wd && path.filename() == event->name) {
    emit_file_changed();
}

Эти события ( IN_MODIFY|IN_CREATE|IN_CLOSE_WRITE) фиксируют методы, которые я опробовал выше ( touch, echo "" >, vim, nano, gedit). Держу пари, что я также смогу захватить измененные символические ссылки с их помощью.

решение2

Или вы можете использовать fatrace, который намного быстрее и не имеет таких проблем, например, что-то вроде этого:

fatrace --timestamp --filter='WD<>+'

Связанный контент