Использование inotifywait вместе с vim

14

У меня есть простой скрипт, который контролирует файл на предмет изменений и rsyncs его с удаленным копированием:

#!/bin/bash

while inotifywait -e close_write somefile
do
    rsync somefile user@host.domain:./somefile
done

Он работает нормально с nano, но не работает с vim. Когда я использую нано, он выводит:

somefile CLOSE_WRITE,CLOSE   

и начинает следующий цикл в ожидании другого издания.

Когда я использую vim, выходных данных нет, скрипт просто закрывается с кодом выхода 0.

Я провел некоторое исследование и выяснил, что close_write - это правильный параметр для использования initofywait вместе с vim (сначала я хотел использовать событие modify), но по какой-то причине он мне не удался.

adam767667
источник
Меня устраивает. Вы изменили файл в vim перед сохранением, а не просто открыли его для редактирования?
Ройма
@roaima Работает только если backupcopyопция отключена.
Жиль "ТАК - перестань быть злым"

Ответы:

15

Редакторы могут использовать несколько стратегий для сохранения файла. Два основных варианта - перезаписать существующий файл или записать в новый файл и переместить его на место. Запись в новый файл и перемещение его на место имеет замечательное свойство, заключающееся в том, что в любой момент времени чтение из файла дает вам полную версию файла (один момент - старый, следующий - новый). Если файл перезаписан на месте, есть время, в течение которого он является неполным, что является проблематичным, если какая-то другая программа обращается к нему только тогда или если происходит сбой системы.

Нано явно перезаписывает существующий файл. Ваш скрипт обнаруживает точку, когда он закончил писать ( close_writeсобытие), и запускается rsyncв этой точке. Обратите внимание, что rsync может получить неполную версию файла, если дважды сохранить его в быстрой последовательности, прежде чем rsync завершит свою работу с первого сохранения.

Vim, с другой стороны, использует стратегию записи-затем-перемещения - что-то вроде эффекта

echo 'new content' >somefile.new
mv -f somefile.new somefile

Что происходит со старой версией файла, так это то, что он удаляется в тот момент, когда новая версия перемещается на место. В этот момент inotifywaitкоманда возвращается, потому что файл, который ей сказали смотреть, больше не существует. (Новый somefile- это другой файл с тем же именем.) Если Vim был настроен для создания файла резервной копии, что-то произойдет,

echo 'new content' >somefile.new
ln somefile somefile.old
mv -f somefile.new somefile

и inotifywaitтеперь будет смотреть резервное копирование.

Для получения дополнительной информации о стратегиях сохранения файлов см. Как можно сделать живое обновление во время работы программы? и файл разрешений и сохранения

Vim можно сказать использовать стратегию перезаписи: отключите backupcopyопцию ( :set nobackupcopy). Это рискованно, как указано выше.

Чтобы справиться с обеими стратегиями сохранения, просмотрите каталог и отфильтруйте их close_writeи moved_toсобытия somefile.

inotifywait -m -e close_write,moved_to --format %e/%f . |
while IFS=/ read -r events file; do
  if [ "$file" = "somefile" ]; then
    …
  fi
done
Жиль "ТАК - прекрати быть злым"
источник
Недостаток метода, предложенного здесь, заключается в том, что когда вы пишете несколько файлов «одновременно», ваши команды будут выполняться один раз для каждого файла. Я редактирую код, поэтому я могу изменить заголовок и пару других блоков перевода и :wa. Затем моя сборка запускается один раз для каждого записанного файла.
Ограниченное искупление
@LimitedAtonement Это гораздо более сложный вариант использования, чем в этом вопросе. В вашем случае вам нужно немного подождать после сохранения одного файла. Файлы никогда не изменяются «сразу». Если вы сохраняете несколько файлов с помощью :wa, вы получаете последовательные события inotify. Вам нужно подождать после первого, чтобы увидеть, придут ли другие. Но вы можете использовать код, представленный здесь: дополнительная сложность будет идти внутри .
Жиль "ТАК - перестань быть злым"
@ Жиль, я решил while true; do inotifywait ... [no -m]; make; sleep .1; done;или около того. Есть некоторые ошибки, но я пришел к чему-то вполне подходящему.
Ограниченное искупление