Как запустить скрипт оболочки при изменении файла или каталога? - PullRequest
57 голосов
/ 30 октября 2010

Я хочу запустить скрипт оболочки при изменении определенного файла или каталога.

Как я могу это легко сделать?

Ответы [ 11 ]

25 голосов
/ 04 декабря 2013

Я использую этот сценарий для запуска сценария сборки изменений в дереве каталогов:

#!/bin/bash -eu
DIRECTORY_TO_OBSERVE="js"      # might want to change this
function block_for_change {
  inotifywait --recursive \
    --event modify,move,create,delete \
    $DIRECTORY_TO_OBSERVE
}
BUILD_SCRIPT=build.sh          # might want to change this too
function build {
  bash $BUILD_SCRIPT
}
build
while block_for_change; do
  build
done

Использует inotify-tools. Проверьте inotifywait man-страницу , чтобы узнать, как настроить то, что вызывает сборку.

21 голосов
/ 30 октября 2010

Использовать inotify-tools .

15 голосов
/ 06 июля 2016

Вы можете попробовать entr инструмент для запуска произвольных команд при изменении файлов. Пример для файлов:

$ ls -d * | entr sh -c 'make && make test'

или

$ ls *.css *.html | entr reload-browser Firefox

или распечатать Changed! при сохранении файла file.txt:

$ echo file.txt | entr echo Changed!

Для каталогов используйте -d, но вы должны использовать его в цикле, например ::

while true; do find path/ | entr -d echo Changed; done

или

while true; do ls path/* | entr -pd echo Changed; done
3 голосов
/ 30 октября 2010

Посмотрите демон монитора файловой системы ядра

http://freshmeat.net/projects/kfsmd/

Вот инструкции:

http://www.linux.com/archive/feature/124903

2 голосов
/ 20 августа 2013

Как насчет этого скрипта? Использует команду stat для получения времени доступа к файлу и запускает команду всякий раз, когда происходит изменение времени доступа (при каждом обращении к файлу).

#!/bin/bash

while true

do

   ATIME=`stat -c %Z /path/to/the/file.txt`

   if [[ "$ATIME" != "$LTIME" ]]

   then

       echo "RUN COMMNAD"
       LTIME=$ATIME
   fi
   sleep 5
done
2 голосов
/ 30 октября 2010

Как уже упоминалось, inotify-tools, вероятно, лучшая идея.Однако, если вы программируете для удовольствия, вы можете попытаться заработать хакерские XP за счет разумного применения tail -f .

1 голос
/ 17 февраля 2019

inotifywait может удовлетворить вас.

Вот типичный пример:

inotifywait -m /path -e create -e moved_to -e close_write | # -m is --monitor, -e is --event
    while read path action file; do
        if [[ "$file" =~ .*rst$ ]]; then             # if suffix is '.rst'
            echo ${path}${file} ': '${action}        # execute your command
            echo 'make html'
            make html
        fi
    done
1 голос
/ 30 октября 2010

Вот еще один вариант: http://fileschanged.sourceforge.net/

См. Особенно «пример 4», который «отслеживает каталог и архивирует любые новые или измененные файлы».

0 голосов
/ 24 ноября 2018

Пример использования inotifywait:

Предположим, я хочу запускать rails test каждый раз, когда я изменяю соответствующий файл.

1. Составьте список соответствующих файлов, которые вы хотите просмотреть:

Вы можете сделать это вручную, но я считаю ack очень полезным для составления этого списка.

ack --type-add=rails:ext:rb,erb --rails -f > Inotifyfile

2. Попросите inotifywait сделать работу

while inotifywait --fromfile Inotifyfile; do rails test; done

Вот и все!

ПРИМЕЧАНИЕ. Если вы используете Vagrant для запуска кода на виртуальной машине, может оказаться полезным расширение mhallin / vagrant-notify-forwarder .

UPDATE: Еще лучше сделать alias и избавиться от файла:

alias rtest="while inotifywait $(ack --type-add=rails:ext:rb,erb --rails -f | tr \\n \ ); do rails test; done"
0 голосов
/ 15 октября 2015

Добавьте в ~ / .bashrc следующее:

function react() {
    if [ -z "$1" -o -z "$2" ]; then
        echo "Usage: react <[./]file-to-watch> <[./]action> <to> <take>"
    elif ! [ -r "$1" ]; then
        echo "Can't react to $1, permission denied"
    else
        TARGET="$1"; shift
        ACTION="$@"
        while sleep 1; do
            ATIME=$(stat -c %Z "$TARGET")
            if [[ "$ATIME" != "${LTIME:-}" ]]; then
                LTIME=$ATIME
                $ACTION
            fi
        done
    fi
}
...