Як виконати команду, коли файл змінюється?


434

Я хочу швидкий і простий спосіб виконання команди щоразу, коли файл змінюється. Я хочу щось дуже просте, те, що я залишатиму працювати на терміналі і закривати його щоразу, коли закінчу працювати з цим файлом.

В даний час я використовую це:

while read; do ./myfile.py ; done

І тоді мені потрібно зайти до цього терміналу і натиснути Enter, коли я зберігаю цей файл у своєму редакторі. Я хочу щось таке:

while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done

Або будь-яке інше таке просте рішення.

BTW: Я використовую Vim, і я знаю, що можу додати автокоманду, щоб запустити щось на BufWrite, але це не таке рішення, яке я хочу зараз.

Оновлення: я хочу чогось простого, якщо можливо. Більше того, я хочу щось запустити в терміналі, тому що я хочу бачити вихід програми (я хочу бачити повідомлення про помилки).

Про відповіді: Дякую за всі ваші відповіді! Усі вони дуже хороші, і кожен з них має зовсім інший підхід від інших. Оскільки мені потрібно прийняти лише одне, я приймаю той, який я фактично використовував (це було просто, швидко і легко запам'ятовується), хоча я знаю, що це не найвишуканіше.


Можливий дублікат між сайтами: stackoverflow.com/questions/2972765/… (хоча тут це на тему =))
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

я посилався перед дублікатом між веб-сайтом, і йому було відмовлено: S;)
Франциско Тапія

4
Рішення Джонатана Хартлі ґрунтується на інших рішеннях тут і виправляє великі проблеми, на які є відповіді, які беруть голосовий голос: відсутні деякі зміни та неефективні. Будь ласка, змініть прийняту відповідь на його, яка також зберігається в github на сайті github.com/tartley/rerun2 (або на якесь інше рішення без цих вад)
nealmcb

Відповіді:


404

Просто, використовуючи inotifywait (встановіть inotify-toolsпакет розповсюдження ):

while inotifywait -e close_write myfile.py; do ./myfile.py; done

або

inotifywait -q -m -e close_write myfile.py |
while read -r filename event; do
  ./myfile.py         # or "./$filename"
done

Перший фрагмент простіший, але він має суттєвий мінус: він буде пропускати зміни, які виконуються під час inotifywaitйого запуску (зокрема, під час myfileзапуску). Другий фрагмент не має цього дефекту. Однак майте на увазі, що воно передбачає, що ім'я файлу не містить пробілів. Якщо це проблема, скористайтеся --formatопцією, щоб змінити вихід, щоб він не включав ім'я файлу:

inotifywait -q -m -e close_write --format %e myfile.py |
while read events; do
  ./myfile.py
done

Так чи інакше, існує обмеження: якщо якась програма замінить myfile.pyінший файл, а не записується на існуючий myfile, inotifywaitзагине. Багато редакторів працюють саме так.

Щоб подолати це обмеження, використовуйте inotifywaitв каталозі:

inotifywait -e close_write,moved_to,create -m . |
while read -r directory events filename; do
  if [ "$filename" = "myfile.py" ]; then
    ./myfile.py
  fi
done

Крім того, використовуйте інший інструмент, який використовує ті ж основні функціональні можливості, як incron (дозволяє реєструвати події, коли файл змінено) або fswatch (інструмент, який також працює у багатьох інших варіантах Unix, використовуючи аналог кожного варіанту Linux inotify).


46
Я все це (з досить декількома баш-трюками) перетворив у простий у користуванні sleep_until_modified.shсценарій, доступний за адресою: bitbucket.org/denilsonsa/small_scripts/src
Denilson Sá Maia

14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; doneце фантастично. Дякую.
Rhys Ulerich

5
inotifywait -e delete_selfздається, працює добре для мене.
Кос

3
Це просто, але має два важливі питання: Події можуть бути пропущені (всі події в циклі), ініціалізація inotifywait робиться щоразу, що робить це рішення повільнішим для великих рекурсивних папок.
Wernight

6
Чомусь while inotifywait -e close_write myfile.py; do ./myfile.py; doneзавжди виходить без запуску команди (bash і zsh). Для цього мені потрібно було додати || true, наприклад: while inotifywait -e close_write myfile.py || true; do ./myfile.py; done
ideaman42

166

entr ( http://entrproject.org/ ) забезпечує більш дружній інтерфейс для ініціації (а також підтримує * BSD & Mac OS X).

Це дозволяє дуже просто вказати кілька файлів для перегляду (обмежено лише ulimit -n), позбавить клопоту від роботи з файлами, що замінюються, і вимагає менше синтаксису bash:

$ find . -name '*.py' | entr ./myfile.py

Я використовую це на моєму цілому дереві джерела проекту для запуску одиничних тестів для коду, який я зараз змінюю, і це вже було величезним стимулом для мого робочого процесу.

Такі прапорці -c(очищення екрана між запусками) та -d(вихід, коли новий файл додається до каталогу, що контролюється) додають ще більшої гнучкості, наприклад, ви можете зробити:

$ while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; done

На початок 2018 року він все ще знаходиться в активному розвитку, і його можна знайти в Debian & Ubuntu ( apt install entr); будівля з авторського репо була в будь-якому випадку безболісною.


3
Не обробляє нові файли та їх модифікації.
Wernight

2
@Wernight - станом на 7 травня 2014 року Entr має новий -dпрапор; це трохи довше, але ви можете зробити, while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; doneщоб мати справу з новими файлами.
Пол Фенні

1
entr також доступний у rebian repos принаймні з debian jessie / 8.2 on ...
Peter V. Mørch

5
найкращий я знайшов на OS X точно. fswatch захоплює занадто багато прикольних подій, і я не хочу витратити час, щоб з'ясувати, чому
dtc

5
Варто зазначити, що entr доступний на Homebrew, тому brew install entrпрацюватиме як очікується
jmarceli

108

Я написав програму Python, щоб зробити саме це, що називалося, коли змінилося .

Використання просте:

when-changed FILE COMMAND...

Або переглянути кілька файлів:

when-changed FILE [FILE ...] -c COMMAND

FILEможе бути каталогом. Дивіться рекурсивно за допомогою -r. Використовуйте %fдля передачі імені файлу команді.


1
@ysangkok так, так, в останній версії коду :)
joh

4
Тепер доступний з "встановлення піп при зміні". Ще добре працює. Дякую.
А. Л. Фланаган

2
Для очищення екрана спочатку можна скористатися when-changed FILE 'clear; COMMAND'.
Дейв Джеймс Міллер

1
Ця відповідь набагато краща, тому що я можу це зробити і в Windows. І цей хлопець насправді написав програму, щоб отримати відповідь.
Wolfpack'08

4
Гарні новини всім! when-changedзараз крос-платформа! Ознайомтеся з останніми 0.3.0 реліз :)
JOH

52

Як щодо цього сценарію? Він використовує statкоманду для отримання часу доступу до файлу та виконує команду кожного разу, коли відбувається зміна часу доступу (щоразу, коли доступ до файлу).

#!/bin/bash

### Set initial time of file
LTIME=`stat -c %Z /path/to/the/file.txt`

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

   if [[ "$ATIME" != "$LTIME" ]]
   then    
       echo "RUN COMMAND"
       LTIME=$ATIME
   fi
   sleep 5
done

2
Чи не буде statвідповідна зміна часу кращою відповіддю "щоразу, коли файл змінюється"?
Xen2050

1
Чи запускає статистику багато разів на секунду, викликає багато читання на диску? чи системний виклик fstat автоматично зробить кешування цих відповідей якось? Я намагаюся написати своєрідний "грухтіння", щоб скласти свій код C, коли я вношу зміни
Оскенсо Каші

Це добре, якщо ви знаєте ім'я файлу, яке слід переглянути заздалегідь. Краще було б передати ім'я файлу до сценарію. Ще краще, якби ви могли передати багато імен файлів (наприклад, "mywatch * .py"). Ще краще, якби це могло працювати і рекурсивно на файлах у підкаталогах, що і деякі інші рішення.
Джонатан Хартлі

5
На всякий випадок, коли хтось цікавиться важким зчитуванням, я перевірив цей сценарій в Ubuntu 17.04 зі сном 0,05 секунди і vmstat -dслідкувати за доступом до диска. Здається, Linux справляє фантастичну роботу з кешування подібних речей: D
Оскенсо Каші

Є помилка в "КОМАНДІ", я намагався виправити, але ТАК говорить: "Редагувати не менше 6 символів"
користувач337085

30

Рішення за допомогою Vim:

:au BufWritePost myfile.py :silent !./myfile.py

Але я не хочу цього рішення, оскільки це начебто дратує тип, трохи важко запам'ятати, що саме вводити, і скасувати його ефекти (важко запустити :au! BufWritePost myfile.py) трохи важко . Крім того, це рішення блокує Vim, поки команда не закінчить виконання.

Тут я додав це рішення просто для повноти, оскільки це може допомогти іншим людям.

Щоб відобразити програмний вихід (і повністю порушити потік редагування, оскільки вихід буде писати над вашим редактором протягом декількох секунд, поки ви не натиснете Enter), видаліть :silentкоманду.


1
Це може бути дуже приємно в поєднанні з entr(див. Нижче) - просто зробіть vim touch фіктивним файлом, який переглядає entr, і дозвольте entr зробити все решта у фоновому режимі ... або tmux send-keysякщо у вас є таке середовище :)
Пол Фенні

приємно! ви можете зробити макрос для свого .vimrcфайлу
ErichBSchulz

23

Якщо у вас випадково npmвстановлено, nodemonце, мабуть, найпростіший спосіб розпочати роботу, особливо в OS X, яка, мабуть, не запускає інструментів. Він підтримує виконання команди при зміні папки.


5
Однак він переглядає лише файли .js та .coffee.
zelk

6
Поточна версія, здається, підтримує будь-яку команду, наприклад: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models
kek

1
Я б хотів, щоб я отримав більше інформації, але у osx є метод відстеження змін, fsevents
ConstantineK

1
В OS X ви також можете використовувати Launch Daemons за допомогою WatchPathsключа, як показано у моєму посиланні.
Адам Джонс

19

Для тих, хто не може встановити, inotify-toolsяк я, це має бути корисним:

watch -d -t -g ls -lR

Ця команда вийде, коли вихід зміниться, ls -lRперерахує кожен файл та каталог з його розмірами та датами, тож якщо файл буде змінено, він повинен вийти з команди, як каже людина:

-g, --chgexit
          Exit when the output of command changes.

Я знаю, що цю відповідь ніхто не може прочитати, але сподіваюся, хтось до неї звернеться.

Приклад командного рядка:

~ $ cd /tmp
~ $ watch -d -t -g ls -lR && echo "1,2,3"

Відкрити інший термінал:

~ $ echo "testing" > /tmp/test

Тепер перший термінал виведе 1,2,3

Простий приклад сценарію:

#!/bin/bash
DIR_TO_WATCH=${1}
COMMAND=${2}

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}

5
Гарний хак. Я протестував, і, схоже, є проблеми, коли список є довгим, а змінений файл виходить за межі екрана. Невелика модифікація може бути приблизно такою: watch -d -t -g "ls -lR tmp | sha1sum"
Atle

3
якщо ви переглядаєте своє рішення щосекунди, воно працює назавжди і запускайте MY_COMMAND, лише якщо зміниться якийсь файл: дивіться -n1 "watch -d -t -g ls -lR && MY_COMMAND"
mnesarco

Моя версія годинника (в Linux watch from procps-ng 3.3.10) приймає плавні секунди за свій інтервал, отже watch -n0.2 ..., опитуватиме кожну п'яту частину секунди. Добре для здорових тестів на підмілісекундну одиницю.
Джонатан Хартлі

15

rerun2( на github ) - це 10-рядковий сценарій Bash форми:

#!/usr/bin/env bash

function execute() {
    clear
    echo "$@"
    eval "$@"
}

execute "$@"

inotifywait --quiet --recursive --monitor --event modify --format "%w%f" . \
| while read change; do
    execute "$@"
done

Збережіть версію github як "повторну" на своєму PATH та запустіть її, використовуючи:

rerun COMMAND

Він запускається COMMAND щоразу, коли у вашому поточному каталозі відбувається подія зміни файлової системи (рекурсивна.)

Про це можуть сподобатися:

  • Він використовує inotify, тому є більш чутливим, ніж опитування. Чудовий для запуску тестів підмілісекундних одиниць або надання файлів крапкових графіків кожного разу, коли ви натискаєте "зберегти".
  • Оскільки це так швидко, вам не доведеться заважати йому казати ігнорувати великі підкаталоги (наприклад, node_modules) лише з міркувань продуктивності.
  • Це надзвичайно чуйно, тому що він запускає inotifywait лише один раз, при запуску, замість того, щоб запускати його, і завдавати дорогого хіта встановлення годин на кожній ітерації.
  • Це всього 12 рядків Баша
  • Оскільки це Bash, він інтерпретує команди, які ви передаєте його точно так, як ніби ви їх набрали у запиті Bash. (Імовірно, це менш круто, якщо ви використовуєте іншу оболонку.)
  • Він не втрачає подій, які трапляються під час виконання COMMAND, на відміну від більшості інших ініціативних рішень на цій сторінці.
  • Під час першої події він вступає у «мертвий період» на 0,15 секунди, протягом якого інші події ігноруються, перш ніж COMMAND запускається рівно один раз. Це так, що шквал подій, спричинений танцем "створити-записати-рухати", який виконує Vi або Emacs під час збереження буфера, не спричинить багаторазових копій можливих повільних тестових наборів. Будь-які події, які потім трапляються під час виконання COMMAND, не ігноруються - вони спричинить другий мертвий період та подальше виконання.

Речі, які можуть не сподобатися в цьому:

  • Він використовує inotify, тому не працює за межами Linuxland.
  • Оскільки він використовує inotify, він намагатиметься переглядати каталоги, що містять більше файлів, ніж максимальна кількість користувачів, які ініціюють годинник. За замовчуванням це, здається, встановлено приблизно від 5000 до 8000 на різних машинах, які я використовую, але їх легко збільшити. Дивіться https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Він не може виконати команди, що містять псевдоніми Bash. Я міг би поклятися, що це працювало раніше. В принципі, тому що це Bash, а не виконуючи COMMAND в нижній частині, я б очікував, що це спрацює. Я хотів би почути, якщо хтось знає, чому це не так. Багато інших рішень на цій сторінці також не можуть виконувати такі команди.
  • Особисто я хотів би, щоб я зміг натиснути клавішу в терміналі, в якому він працює, щоб вручну викликати додаткове виконання COMMAND. Чи можу я додати це якось просто? Одночасно виконується цикл "read -n1", який також виконує дзвінки?
  • Зараз я зашифрував його, щоб очистити термінал і надрукувати виконану КОМАНДУ на кожній ітерації. Деякі люди можуть хотіти додати прапори командного рядка, щоб вимкнути подібні речі тощо. Але це збільшить розмір та складність у багато разів.

Це уточнення передвісті @ cychoi.


2
Я вважаю, що вам слід використовувати "$@"замість $@, щоб правильно працювати з аргументами, що містять пробіли. Але в той же час ви користуєтесь eval, що змушує користувача повторної роботи бути дуже обережним при цитуванні.
Denilson Sá Maia

Спасибі Денілсон. Не могли б ви навести приклад того, де цитування потрібно робити обережно? Я використовував його останні 24 години і досі не бачив жодних проблем із пробілами, а також ретельно не цитував нічого - просто викликав як rerun 'command'. Ви просто говорите, що якщо я використовував "$ @", то користувач міг би викликати як rerun command(без лапок?), Що для мене не здається корисним: я, як правило, не хочу, щоб Bash виконував будь-яку обробку команди, перш ніж передавати її щоб повторити. наприклад, якщо команда містить "echo $ myvar", то я хочу побачити нові значення myvar у кожній ітерації.
Джонатан Хартлі

1
Щось подібне rerun foo "Some File"може зламатися. Але оскільки ви використовуєте eval, його можна переписати як rerun 'foo "Some File". Зауважте, що іноді розширення контуру може ввести пробіли: rerun touch *.fooшвидше за все, воно порушиться, а використання rerun 'touch *.foo'має дещо іншу семантику (розширення контуру відбувається лише один раз або кілька разів).
Denilson Sá Maia

Дякую за допомогу. Так: rerun ls "some file"перерви через пробіли. rerun touch *.foo*Зазвичай працює нормально, але виходить з ладу, якщо імена файлів, які відповідають * .foo, містять пробіли. Дякую, що допомогли мені побачити, як rerun 'touch *.foo'відрізняється семантика, але я підозрюю, що версія з одинарними цитатами є семантичною, яку я хочу: я хочу, щоб кожна ітерація повторення діяла так, ніби я знову набрав команду - отже, я хочу *.foo розширюватись на кожній ітерації . Я спробую ваші пропозиції вивчити їх вплив ...
Джонатан Хартлі,

Більше обговорення цього PR ( github.com/tartley/rerun2/pull/1 ) та інших.
Джонатан Хартлі

12

Ось простий скрипт оболонки Bourne shell, який:

  1. Бере два аргументи: файл, що підлягає моніторингу, і команду (при необхідності аргументи)
  2. Копіює файл, який ви контролюєте, у каталог / tmp
  3. Перевіряє кожні дві секунди, щоб побачити, чи файл, який ви відстежуєте, новіший за копію
  4. Якщо він новіший, він перезаписує копію новим оригіналом і виконує команду
  5. Очищається після себе, якщо натиснути Ctr-C

    #!/bin/sh  
    f=$1  
    shift  
    cmd=$*  
    tmpf="`mktemp /tmp/onchange.XXXXX`"  
    cp "$f" "$tmpf"  
    trap "rm $tmpf; exit 1" 2  
    while : ; do  
        if [ "$f" -nt "$tmpf" ]; then  
            cp "$f" "$tmpf"  
            $cmd  
        fi  
        sleep 2  
    done  
    

Це працює на FreeBSD. Єдине питання переносимості, про який я можу подумати, - це те, якщо якийсь інший Unix не має команди mktemp (1), але в цьому випадку ви можете просто жорстко кодувати ім'я файлу temp.


9
Опитування - це єдиний портативний спосіб, але більшість систем мають механізм сповіщення про зміни файлів (ініціювати в Linux, kqueue на FreeBSD, ...). У вас виникає серйозна проблема з котируванням $cmd, але, на щастя, це легко виправити: викопати cmdзмінну та виконати "$@". Ваш сценарій не підходить для моніторингу великого файлу, але це може бути виправлено заміною cpна touch -r(потрібна лише дата, а не вміст). Для -ntперевірки потрібен тест bash, ksh або zsh.
Жиль

8

Погляньте на інкрон . Він схожий на cron, але замість часу використовує ініціативні події.


Це може бути спрацьоване, але створення запису про інкор є досить трудомістким процесом порівняно з іншими рішеннями на цій сторінці.
Джонатан Хартлі

6

Ще одне рішення з NodeJs, fsmonitor :

  1. Встановити

    sudo npm install -g fsmonitor
    
  2. З командного рядка (наприклад, журнали моніторів та "роздріб", якщо змінюється один файл журналу)

    fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
    

Побічна примітка: приклад можна було б вирішити tail -F -q *.log, я думаю.
Volker Siegel

Це було лише навести приклад, tail -fчи не clearтермінал.
Атіка

6

Подивіться на Guard, зокрема з цим плагіном:

https://github.com/hawx/guard-shell

Ви можете налаштувати його для перегляду будь-якої кількості шаблонів у каталозі вашого проекту та виконання команд при змінах. Хороший шанс навіть у тому, що доступний плагін для того, що ви намагаєтесь зробити в першу чергу.


6

якщо у вас встановлений nodemon , ви можете зробити це:

nodemon -w <watch directory> -x "<shell command>" -e ".html"

У моєму випадку я редагую html локально та надсилаю його на віддалений сервер, коли файл змінюється.

nodemon -w <watch directory> -x "scp filename jaym@jay-remote.com:/var/www" -e ".html"

6

Під Linux:

man watch

watch -n 2 your_command_to_run

Виконуватиме команду кожні 2 секунди.

Якщо вашій команді потрібно більше 2 секунд для запуску, годинник буде чекати, поки це виконано, перш ніж зробити це знову.


Це досить просто, хоч і дещо марно, але це легко для таких завдань розвитку, як зміни живих змін у стилі.
Xeoncross

2
Що відбувається, коли команді на виконання потрібно більше двох секунд?
тридцять тридцять

@thirtythreeforty Швидкий експеримент на Ubuntu показує, що годинник буде чекати цілих дві секунди незалежно від того, скільки часу запускає команда. FWIW, період сну може бути вказаний за допомогою '-n' до мінімум 0,1 секунди.
Джонатан Хартлі

5

Watchdog - це проект Python, і він може бути саме тим, що ви шукаєте:

Підтримувані платформи

  • Linux 2.6 (ініціювати)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW з портами завершення вводу / виводу; робочі потоки ReadDirectoryChangesW)
  • Незалежна від ОС (опитування диска на знімки каталогів та періодичне їх порівняння; повільне та не рекомендується)

Щойно написав обгортку командного рядка для цього watchdog_exec:

Приклад виконання

У події fs, що включає файли та папки в поточному каталозі, запустіть echo $src $dstкоманду, якщо вона не змінила подія fs, тоді запустіть python $srcкоманду.

python -m watchdog_exec . --execute echo --modified python

Використання коротких аргументів та обмеження виконання лише тоді, коли події включають " main .py":

python -m watchdog_exec . -e echo -a echo -s __main__.py

EDIT: Щойно знайдений Watchdog має офіційний CLI watchmedo, тому також перевірте це.


4

Якщо ваша програма генерує якийсь журнал / вихід, ви можете створити Makefile з правилом для цього журналу / виводу, який залежить від вашого сценарію, і зробити щось подібне

while true; do make -s my_target; sleep 1; done

Крім того, ви можете створити фальшиву ціль і мати для неї правило як викликати ваш скрипт, так і торкатися фальшивої цілі (хоча це все ще залежить від вашого сценарію).


11
while sleep 1 ; do something ; doneтрохи краще, ніж while true ; do something ; sleep 1 ; done. Принаймні, вона легко зупиняється при натисканні клавіш Ctrl + C.
Denilson Sá Maia

Чи вимкнення сну спричинить зайнятий цикл (процесор генерує тепло і шкодить автономному режиму на ноутбуці)?
Стівен Лу

2
@StevenLu: ні, сон не є зайнятим чеканням. Проблема полягає в тому, що якщо сон знаходиться в організмі, Control-C знищить сон і петля почне запускатися. Енергоспоживання при запуску циклу незначне. Спробуйте самі в терміналі. Вам потрібно потримати Control-C, щоб він працював, якщо у вас спав організм.
Янус Трольсен

Правильно. Я думаю, що я пропустив це і не побачив, що сон все ще присутній як стан петлі. Цей маленький твіст досить приголомшливий.
Стівен Лу

4

2
Це багатофункціональний сценарій Bash у 200 рядках, який опитується statза вказаними іменами, працює md5sumна виході та запускає дану команду, якщо це значення змінюється. Оскільки це Bash, я підозрюю, що це добре спрацює із заданою командою точно так, як якщо б ви ввели її в підказці Bash. (На відміну від більшості рішень, написаних іншими мовами, не вдасться виконати команди, які, наприклад, містять псевдоніми оболонки, такі як ll)
Джонатан Хартлі

4

Удосконалено на відповідь Гілла .

Ця версія запускається inotifywaitодин раз і відстежує події (.eg:) modifyпісля цього. Таке, що inotifywait не потрібно повторно виконувати при кожній зіткнутій події.

Це швидко і швидко! (навіть при рекурсивному моніторингу великого каталогу)

inotifywait --quiet --monitor --event modify FILE | while read; do
    # trim the trailing space from inotifywait output
    REPLY=${REPLY% }
    filename=${REPLY%% *}
    # do whatever you want with the $filename
done

Це найкраща відповідь на сторінці для користувачів лише для Linux. Замініть матеріал у циклі на "Execute $ @", і користувач може викликати цей сценарій, передаючи свою команду для запуску. Він навіть працює з командами, що містять псевдоніми оболонки, якщо ви його джерелом, використовуючи щось на зразок ". Ім'я сценарію COMMAND". Це ім’я все ще знайдеться на PATH.
Джонатан Хартлі

Я думаю, ти маєш на увазі поставити "під час читання ЗАВДАННЯ"?
Джонатан Хартлі

1
дякую за роз’яснення Невдячні за його поетапність! Я б видалив ці коментарі, але, звичайно, зараз не стану.
Джонатан Хартлі

3

Ще трохи з боку програмування, але ви хочете щось подібне до ініціації . Є реалізація на багатьох мовах, таких як jnotify та pyinotify .

Ця бібліотека дозволяє відстежувати окремі файли або цілі каталоги та повертає події під час виявлення дії. Інформація, що повертається, включає іншу назву файла, дію (створення, зміна, перейменування, видалення) та шлях до файлу, серед іншої корисної інформації.


3

Для тих, хто шукає рішення для FreeBSD, ось порт:

/usr/ports/sysutils/wait_on

3

Мені подобається простота, while inotifywait ...; do ...; doneпроте у неї є два питання:

  • Зміни файлів, які відбуваються під час do ...;заповіту, будуть пропущені
  • Повільний при використанні в рекурсивному режимі

Для цього я створив допоміжний скрипт, який використовує inotifywait без цих обмежень: inotifyexec

Я пропоную вам поставити цей сценарій на свій шлях, як в ~/bin/. Використання описується просто запуском команди.

Приклад: inotifyexec "echo test" -r .


Оновлено скрипт для підтримки відповідності шаблонів регулярних виразів.
Wernight

Обидві проблеми вирішуються за допомогою inotifywait в режимі "--monitor". Дивіться відповідь cychoi.
Джонатан Хартлі

3

Удосконалено рішення Себастьяна за допомогою watchкоманди:

watch_cmd.sh:

#!/bin/bash
WATCH_COMMAND=${1}
COMMAND=${2}

while true; do
  watch -d -g "${WATCH_COMMAND}"
  ${COMMAND}
  sleep 1     # to allow break script by Ctrl+c
done

Приклад дзвінка:

watch_cmd.sh "ls -lR /etc/nginx | grep .conf$" "sudo service nginx reload"

Це працює, але будьте обережні: watchкоманда має відомі помилки (див. Man): вона реагує на зміни лише в VISIBLE в кінцевих частинах -g CMDвиводу.


2

Ви можете спробувати рефлекс .

Reflex - це невеликий інструмент для перегляду каталогу та повторної роботи команди при зміні певних файлів. Він чудово підходить для автоматичного виконання завдань компіляції / вкладишів / тестів та для перезавантаження програми, коли код змінюється.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make

Чи можете ви процитувати / пояснити трохи про інструмент? Швидко прочитайте, як рекомендувати програмне забезпечення для керівництва.
bertieb

1

Відповідь в одній лінійці, яку я використовую для відстеження зміни файлу:

$ while true ; do NX=`stat -c %Z file` ; [[ $BF != $NX ]] && date >> ~/tmp/fchg && BF=$NX || sleep 2 ; done

Вам не потрібно ініціалізувати BF, якщо ви знаєте, що перший день - це час початку.

Це просто і портативно. Тут є ще одна відповідь, заснована на тій же стратегії, що використовує тут скрипт. Погляньте також.


Використання: я використовую це для налагодження та стеження за цим ~/.kde/share/config/plasma-desktop-appletsrc; що з незрозумілої причини продовжує втрачати моюSwitchTabsOnHover=false


1

Я використовую цей сценарій для цього. Я використовую inotify в режимі монітора

#!/bin/bash
MONDIR=$(dirname $1)
ARQ=$(basename $1)

inotifywait -mr -e close_write $MONDIR | while read base event file 
do
  if (echo $file |grep -i "$ARQ") ; then
    $1
  fi
done

Збережіть це як runatwrite.sh

Usage: runatwrite.sh myfile.sh

він буде працювати myfile.sh при кожному записі.


1

Для тих, хто використовує OS X, ви можете використовувати LaunchAgent, щоб переглянути шлях / файл за змінами та зробити щось, коли це станеться. FYI - LaunchControl - це хороший додаток для легкого виготовлення / модифікації / видалення демонів / агентів.

( приклад взятий звідси )

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>test</string>
    <key>ProgramArguments</key>
    <array>
        <string>say</string>
        <string>yy</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>~/Desktop/</string>
    </array>
</dict>
</plist>


0

Для людей, які виявили це в Google, щоб змінити певний файл, відповідь набагато простіша (натхненна відповіддю Гілла ).

Якщо ви хочете щось зробити після того, як певний файл був записаний, ось як:

while true; do
  inotifywait -e modify /path/to/file
  # Do something *after* a write occurs, e.g. copy the file
  /bin/cp /path/to/file /new/path
done

Збережіть це, наприклад, copy_myfile.shі помістіть .shфайл у /etc/init.d/папку для запуску при запуску.


Ділиться проблемою з відповіддю Джайлса, що він працює inotifywait на кожну ітерацію, яка може бути невідповідною для рекурсивного перегляду дуже великих каталогів. Дивіться відповідь cychoi для виправлення цього.
Джонатан Хартлі

0

Інструмент «фідо» може бути ще одним варіантом для цієї потреби. Дивіться https://www.joedog.org/fido-home/


Будь ласка, прочитайте Як я рекомендую програмне забезпечення для деяких порад щодо того, як вам рекомендувати рекомендувати програмне забезпечення. Ви повинні надати принаймні посилання, додаткову інформацію про саме програмне забезпечення та про те, як його можна використовувати для вирішення проблеми у питанні.
DavidPostill

0

Як і кілька інших, я також написав легкий інструмент командного рядка для цього. Це повністю документально підтверджене, перевірене та модульне.

Дивіться

Установка

Ви можете встановити його (якщо у вас Python3 та pip), використовуючи:

pip3 install git+https://github.com/vimist/watch-do

Використання

Скористайтеся ним відразу, запустивши:

watch-do -w my_file -d 'echo %f changed'

Огляд функцій

  • Підтримується глобалізація файлів (використання -w '*.py'або -w '**/*.py')
  • Запустіть кілька команд щодо зміни файлу (просто вкажіть -dпрапор ще раз)
  • Динамічно підтримує список файлів для перегляду, якщо використовується глобул (-r щоб увімкнути це)
  • Кілька способів "переглянути" файл:
    • Час модифікації (за замовчуванням)
    • Файл хешу
    • Тривіально реалізувати своє власне (це спостерігач ModificationTime )
  • Модульний дизайн. Якщо ви хочете запускати команди, коли доступ до файлу, тривіально написати власний вахтувальний механізм (механізм, який визначає, чи потрібно запускати виконавці).
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.