
Gostaria de monitorar um arquivo com inotify
e acionar algum código quando alguém altera o conteúdo ( IN_MODIFY
ou IN_CLOSE_WRITE
), mas estou tendo problemas onde inotify
para de retornar eventos quando os usuários editam o arquivo com sua ferramenta favorita. O arquivo deve ser simples (linha única, sem espaços, no máximo 20 caracteres). Prefiro não restringir seu uso, mas não sei como lidar com diferentes situações.
Estou usando inotify
e estes são os eventos que recebo quando vários aplicativos editam o arquivo:
Ação | inotify Eventos |
---|---|
touch file |
IN_OPEN |
echo "data" > file |
IN_MODIFY , IN_OPEN , IN_ACCESS , entãoIN_CLOSE_NOWRITE |
nano file (em aberto) |
IN_OPEN |
nano file (sobre ^O ) |
IN_MODIFY , IN_CLOSE_WRITE , IN_OPEN ,IN_ACCESS |
vim file (em aberto) |
IN_OPEN ,IN_CLOSE_NOWRITE |
vim file (sobre :w ) |
IN_MOVE_SELF , IN_ATTRIB , então os eventos param de vir deste arquivo |
gedit file (em aberto) |
IN_OPEN , IN_CLOSE_NOWRITE ,IN_ACCESS |
gedit file (ao salvar) |
IN_OPEN , IN_CLOSE_WRITE , IN_ATTRIB , então os eventos param de vir deste arquivo |
mv newfile file |
IN_ATTRIB , então os eventos param de vir deste arquivo |
A certa altura, pensei ter visto gedit
o gatilho também disparar IN_DELETE_SELF
antes de ficar em silêncio.
No caso em que um usuário usa vim
e gedit
, paro de receber inotify
eventos depois que o usuário termina as edições. Como devo lidar com isso?
A única coisa que vejo em comum é o IN_ATTRIB
evento. Suspeito que quando receber o IN_ATTRIB
evento, devo fazer inotify_rm_watch()
isso wd
e depois recriar um novo inotify_add_watch()
com base no mesmo caminho. Mas essa é a abordagem correta?
Outra opção poderia ser observar o diretório pai. O nome do arquivo afetado está incluído no arquivo inotify_event::name
e, portanto, eu poderia filtrar o arquivo de interesse e acionar qualquer um IN_MODIFY
ou IN_CLOSE_WRITE
onde name
corresponda ao meu arquivo de interesse.
Responder1
Como menciona ikkachu, alguns editores criam um novo arquivo e depois substituem o original, alterando o inode. Isso significa que todos os relógios no descritor de relógio original expirarão.
A resposta é examinar o diretório pai e verificar se há alterações em qualquer arquivo com o nome de destino. Algo assim:
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();
}
Esses eventos ( IN_MODIFY|IN_CREATE|IN_CLOSE_WRITE
) capturam as técnicas que tentei acima ( touch
, echo "" >
, vim
, nano
, gedit
). Aposto que também poderia capturar links simbólicos alterados com eles.
Responder2
Ou você pode usar o fatrace, que é muito mais rápido e não apresenta esses problemas, por exemplo, algo assim:
fatrace --timestamp --filter='WD<>+'