Сделать inotifywait группировать несколько обновлений файлов в один?

У меня есть папка с документами Sphinx, которые я просматриваю с помощью inotifywait (из inotify-tools). Скрипт перестраивает html и singlehtml и обновляет Chrome.

#!/bin/sh
inotifywait -mr source --exclude _build -e close_write -e create -e delete -e move | while read file event; do
    make html singlehtml
    xdotool search --name Chromium key --window %@ F5
done

Это отлично работает, когда я сохраняю один файл. Однако, когда я hg update перехожу к старой версии или вставляю несколько файлов в папку source, скрипт запускает каждый файл.

Есть ли простой обходной путь (без написания пользовательских сценариев Python - это я могу сделать), чтобы заставить его ждать долю секунды перед запуском сценария?


person culebrón    schedule 13.08.2012    source источник
comment
См. также: unix.stackexchange.com/q/390914/26227   -  person Dave Jarvis    schedule 23.06.2019


Ответы (2)


Я сделал немного более сложный шелл-скрипт и разместил его в статье:

inotifywait -mr source --exclude _build -e close_write -e create -e delete -e move --format '%w %e %T' --timefmt '%H%M%S' | while read file event tm; do
    current=$(date +'%H%M%S')
    delta=`expr $current - $tm`
    if [ $delta -lt 2 -a $delta -gt -2 ] ; then
        sleep 1  # sleep 1 set to let file operations end
        make html singlehtml
        xdotool search --name Chromium key --window %@ F5
    fi
done

Это делает журнал inotifywait не только именем файла и действием, но и отметкой времени. Скрипт сравнивает временную метку с текущим unixtime и, если разница меньше 2 секунд, запускает make html. Но перед этим он спит 1 секунду, чтобы завершить операции с файлами. Для следующих измененных файлов временная метка будет старой, дельта будет более 2 секунд, и ничего не будет сделано.

Я обнаружил, что этот способ был наименее потребляющим ЦП и наиболее надежным.

Я также пытался запустить простой скрипт Python, но это означало, что если я вставлю в папку что-то такое большое, как jQueryUI, будет порождена тысяча процессов, которые затем станут зомби.

person culebrón    schedule 09.10.2012

Попробуй это:

last_update=0
inotifywait -mr source --exclude _build -e close_write -e create \
    -e delete -e move --format '%T' --timefmt '%s' |
    while read timestamp; do
        if test $timestamp -ge $last_update; then
            sleep 1
            last_update=$(date +%s)
            make html singlehtml
            xdotool search --name Chromium key --window %@ F5
        fi
    done
  1. --format '%T' --timefmt '%s' заставляет выводить метку времени для каждого события.
  2. test $timestamp -ge $last_update сравнивает отметку времени события с отметкой времени последнего обновления. Таким образом, любые события, произошедшие во время сна, пропускаются.
  3. sleep 1 добавляется для ожидания накопления событий. Здесь может быть уместна более короткая продолжительность, например sleep 0.5, но она менее переносима.
  4. last_update=$(date +%s%N) устанавливает отметку времени для последнего обновления, которое будет сравниваться с отметкой времени следующего события. Таким образом, любые дополнительные события, которые происходят во время sleep 1, отбрасываются во время следующей итерации цикла.

Обратите внимание, что здесь есть состояние гонки, потому что strftime() не поддерживает наносекунды. Этот пример может выполняться make дважды, если группа событий пересекает вторую границу. Чтобы избежать пропуска событий, замените -ge на -gt.

person Owen T. Heisler    schedule 12.08.2017
comment
Я думаю, что last_update не установлен для последующих событий. Я пробовал что-то подобное, и это не сработало. Я бы предложил лучше пойти с принятым ответом - person Rakesh N; 30.08.2018
comment
Переменная last_update предназначена не для каждого события, а для каждого выполнения команды обновления. Затем значение last_update сравнивается с отметкой времени каждого последующего события, чтобы определить, нужна ли еще одна команда обновления. Я попытался уточнить это выше. - person Owen T. Heisler; 31.08.2018
comment
Я также переместил sleep 1 в оператор if, где он должен быть, чтобы предотвратить засыпание для каждого отдельного события. - person Owen T. Heisler; 31.08.2018