43.20 Уведомления об изменениях файлов

Некоторые операционные системы поддерживают наблюдение за файловыми системами на предмет изменений файлов или их атрибутов. При правильной настройке Emacs статически связывает соответствующую библиотеку, такую как inotify, kqueue, gfilenotify или w32notify. Эти библиотеки позволяют наблюдать за файловыми системами на локальной машине.

Также возможно наблюдение за файловыми системами на удалённых машинах; см. Удалённые файлы в Руководстве по GNU Emacs. Это не зависит ни от одной из библиотек, связанных с Emacs.

Поскольку все эти библиотеки генерируют различные события при уведомлении об изменениях файлов, Emacs предоставляет специальную библиотеку filenotify, которая представляет унифицированный интерфейс для приложений. Lisp-программы, которые хотят получать уведомления о файлах, должны всегда использовать эту библиотеку вместо нативных. В этом разделе документируются функции и переменные библиотеки filenotify.

Функция: file-notify-add-watch файл флаги обратный-вызов

Добавляет наблюдение за событиями файловой системы, относящимися к файлу. Это обеспечивает передачу событий файловой системы, относящихся к файлу, в Emacs.

Возвращаемое значение — это дескриптор добавленного наблюдения. Его тип зависит от используемой библиотеки, и в общем случае нельзя предполагать, что это целое число, как в примере ниже. Его следует использовать только для сравнения с помощью equal.

Если за файлом по какой-либо причине невозможно наблюдать, эта функция сигнализирует об ошибке file-notify-error.

Иногда за смонтированными файловыми системами невозможно наблюдать на предмет изменений файлов. Это не обнаруживается данной функцией, поэтому возвращаемое значение, не равное nil, не гарантирует, что изменения в файле будут действительно уведомлены.

Если файл является символической ссылкой, эта ссылка не разыменовывается. Наблюдение ведётся именно за самим файлом.

флаги — это список условий, определяющих, за чем вести наблюдение. Он может содержать следующие символы:

change

наблюдать за изменениями содержимого файла

attribute-change

наблюдать за изменениями атрибутов файла, таких как права доступа или время изменения

Если файл является каталогом, change отслеживает создание и удаление файлов в этом каталоге. Некоторые нативные библиотеки уведомлений о файлах также сообщают об изменениях файлов в этом случае. Это не работает рекурсивно.

При возникновении любого события Emacs вызовет функцию обратный-вызов, передав ей единственный аргумент событие вида

(дескриптор действие файл [файл1])

дескриптор — это тот же объект, который возвращает данная функция. действие — это описание события. Оно может быть одним из следующих символов:

created

файл был создан

deleted

файл был удалён

changed

содержимое файла изменилось; в библиотеке w32notify также сообщает об изменениях атрибутов

renamed

файл был переименован в файл1

attribute-changed

изменился атрибут файла

stopped

наблюдение за файлом остановлено

Обратите внимание, что библиотека w32notify не сообщает о событиях attribute-changed. Когда изменяется какой-либо атрибут файла, например права доступа или время изменения, эта библиотека сообщает о событии changed. Аналогично, библиотека kqueue не надёжно сообщает об изменениях атрибутов файлов при наблюдении за каталогом.

Событие stopped означает, что наблюдение за файлом было прекращено. Это может произойти, если была вызвана file-notify-rm-watch (см. ниже), или если наблюдаемый файл был удалён, или файловая система наблюдаемого файла была размонтирована, либо из-за другой ошибки, сообщённой нижележащей библиотекой, которая делает дальнейшее наблюдение невозможным.

файл и файл1 — это имена файлов, о событии которых сообщается. Например:

(require 'filenotify)
     ⇒ filenotify

(defun my-notify-callback (событие)
  (message "Событие %S" событие))
     ⇒ my-notify-callback

(file-notify-add-watch
  "/tmp" '(change attribute-change) 'my-notify-callback)
     ⇒ 35025468

(write-region "foo" nil "/tmp/foo")
     ⇒ Событие (35025468 created "/tmp/.#foo")
        Событие (35025468 created "/tmp/foo")
        Событие (35025468 changed "/tmp/foo")
        Событие (35025468 deleted "/tmp/.#foo")

(write-region "bla" nil "/tmp/foo")
     ⇒ Событие (35025468 created "/tmp/.#foo")
        Событие (35025468 changed "/tmp/foo")
        Событие (35025468 deleted "/tmp/.#foo")

(set-file-modes "/tmp/foo" (default-file-modes) 'nofollow)
     ⇒ Событие (35025468 attribute-changed "/tmp/foo")

Возврат действия renamed зависит от используемой библиотеки наблюдения. В противном случае действия deleted и created могут возвращаться в случайном порядке.

(rename-file "/tmp/foo" "/tmp/bla")
     ⇒ Событие (35025468 renamed "/tmp/foo" "/tmp/bla")

(delete-file "/tmp/bla")
     ⇒ Событие (35025468 deleted "/tmp/bla")
Функция: file-notify-rm-watch дескриптор

Удаляет существующее наблюдение за файлом, заданное его дескриптором. дескриптор должен быть объектом, возвращённым file-notify-add-watch.

Команда: file-notify-rm-all-watches

Удаляет все существующие наблюдения за уведомлениями о файлах из Emacs.

Используйте эту команду с осторожностью, так как она может иметь неожиданные побочные эффекты для пакетов, полагающихся на наблюдения за файлами. Она предназначена главным образом для целей отладки или когда Emacs завис.

Функция: file-notify-valid-p дескриптор

Проверяет наблюдение, заданное его дескриптором, на действительность. дескриптор должен быть объектом, возвращённым file-notify-add-watch.

Наблюдение может стать недействительным, если файл или каталог, за которым оно ведётся, будет удалён, или если поток наблюдателя завершится аномально по любой другой причине. Удаление наблюдения вызовом file-notify-rm-watch также делает его недействительным.

(make-directory "/tmp/foo")
     ⇒ Событие (35025468 created "/tmp/foo")

(setq desc
      (file-notify-add-watch
        "/tmp/foo" '(change) 'my-notify-callback))
     ⇒ 11359632

(file-notify-valid-p desc)
     ⇒ t

(write-region "bla" nil "/tmp/foo/bla")
     ⇒ Событие (11359632 created "/tmp/foo/.#bla")
        Событие (11359632 created "/tmp/foo/bla")
        Событие (11359632 changed "/tmp/foo/bla")
        Событие (11359632 deleted "/tmp/foo/.#bla")

;; Удаление файла в каталоге не делает наблюдение недействительным.
(delete-file "/tmp/foo/bla")
     ⇒ Событие (11359632 deleted "/tmp/foo/bla")

(write-region "bla" nil "/tmp/foo/bla")
     ⇒ Событие (11359632 created "/tmp/foo/.#bla")
        Событие (11359632 created "/tmp/foo/bla")
        Событие (11359632 changed "/tmp/foo/bla")
        Событие (11359632 deleted "/tmp/foo/.#bla")

;; Удаление каталога делает наблюдение недействительным.
;; События поступают для разных дескрипторов наблюдения.
(delete-directory "/tmp/foo" 'recursive)
     ⇒ Событие (35025468 deleted "/tmp/foo")
        Событие (11359632 deleted "/tmp/foo/bla")
        Событие (11359632 deleted "/tmp/foo")
        Событие (11359632 stopped "/tmp/foo")

(file-notify-valid-p desc)
     ⇒ nil