Как запустить скрипт оболочки, когда файл или каталог меняется?
Вопрос
Я хочу запустить сценарий оболочки, когда определенный файл или изменения каталога.
Как я могу легко сделать это?
Решение
Использовать inotify-tools..
Другие советы
Я использую этот скрипт для запуска скрипта сборки на изменениях дерева каталога:
#!/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
человек страницы Для того, как настроить то, что запускает сборку.
Вы можете попробовать 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
Как уже упоминалось, Intify-Tools, вероятно, является лучшей идеей. Однако, если вы программируете для удовольствия, вы можете попробовать и зарабатывать хакер XPS путем разумного применения хвост -f. .
Как насчет этого скрипта? Использует команду «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
Вот еще один вариант: http://fileschanged.sourceforge.net/
См. Особенно «Пример 4», который «контролирует каталог и архивы любые новые или измененные файлы».
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
Просто для целей отладки, когда я пишу скрипт оболочки и хочу, чтобы он запустился на сохранении, я использую это:
#!/bin/bash
file="$1" # Name of file
command="${*:2}" # Command to run on change (takes rest of line)
t1="$(ls --full-time $file | awk '{ print $7 }')" # Get latest save time
while true
do
t2="$(ls --full-time $file | awk '{ print $7 }')" # Compare to new save time
if [ "$t1" != "$t2" ];then t1="$t2"; $command; fi # If different, run command
sleep 0.5
done
Запустите его как
run_on_save.sh myfile.sh ./myfile.sh arg1 arg2 arg3
Редактировать: Выше тестировали на Ubuntu 12.04, для Mac OS, измените линии LS к:
"$(ls -lT $file | awk '{ print $8 }')"
Добавьте следующее в ~ / .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
}
Пример использования 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 для запуска кода на VM, вы можете найти полезные MHALLIN / VAGRANT-NOTION-INVERVER расширение.
ОБНОВИТЬ:Еще лучше, сделай alias
и избавиться от файла:
alias rtest="while inotifywait $(ack --type-add=rails:ext:rb,erb --rails -f | tr \\n \ ); do rails test; done"