Как выполнить команду при изменении файла?

434

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

В настоящее время я использую это:

while read; do ./myfile.py ; done

А затем мне нужно перейти к этому терминалу и нажимать Enterвсякий раз, когда я сохраняю этот файл в моем редакторе. Я хочу что-то вроде этого:

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

Или любое другое решение, столь же простое.

Кстати: я использую Vim, и я знаю, что могу добавить автокоманду для запуска чего-либо на BufWrite, но сейчас это не то решение, которое мне нужно.

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

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

Денилсон Са Майя
источник
Возможный дубликат сайта: stackoverflow.com/questions/2972765/…… (хотя здесь это по теме =))
Ciro Santilli 11 改造 中心 法轮功 六四 事件
Я ссылался перед дублированием сайта, и было отказано: S;)
Франциско Тапиа
4
Решение Джонатана Хартли основывается здесь на других решениях и устраняет большие проблемы, которые возникают у лидеров с наибольшим количеством голосов: отсутствие некоторых модификаций и неэффективность. Пожалуйста, измените принятый ответ на его, который также поддерживается на github на github.com/tartley/rerun2 (или на другое решение без этих недостатков)
nealmcb

Ответы:

405

Просто с помощью 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 с использованием аналога каждого варианта inotify Linux).

жилль
источник
46
Я инкапсулировал все это (с помощью нескольких приемов bash) в простом в использовании sleep_until_modified.shсценарии, доступном по адресу: bitbucket.org/denilsonsa/small_scripts/src
Денилсон Са Майя
14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; doneЭто фантастично. Спасибо.
Рис Улерих
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
ideasman42
166

entr ( http://entrproject.org/ ) предоставляет более дружественный интерфейс для inotify (а также поддерживает * 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 также доступен в репозиториях Debian по крайней мере из Debian Jessie / 8.2 на ...
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
Теперь доступно из "pip install When-Changed". Все еще работает хорошо. Благодарю.
А.Л. Фланаган
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
VDR
источник
2
Разве statизмененное время не будет лучшим ответом «всякий раз, когда файл меняется»?
Xen2050
1
Будет ли запуск статистики много раз в секунду вызывать много чтений на диск? или системный вызов fstat как-то автоматически сделает кеш этих ответов? Я пытаюсь написать что-то вроде «ворчливых часов», чтобы скомпилировать мой код c всякий раз, когда я делаю изменения
Oskenso Kashi
Это хорошо, если вы знаете имя файла для просмотра заранее. Лучше было бы передать имя файла в сценарий. Еще лучше было бы, если бы вы могли передать много имен файлов (например, "mywatch * .py"). Еще лучше было бы, если бы он мог рекурсивно работать с файлами в подкаталогах, как это делают некоторые другие решения.
Джонатан Хартли
5
На всякий случай, если кому-то интересно узнать о тяжелом чтении, я протестировал этот скрипт в Ubuntu 17.04 со сном 0,05 с и vmstat -dследил за доступом к диску. Похоже, linux
отлично
В «КОМАНДЕ» есть опечатка, которую я пытался исправить, но ТАК говорит, что «Правка должна быть не менее 6 символов»
user337085
30

Решение с использованием Vim:

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

Но я не хочу, чтобы это решение, потому что это немного раздражает при наборе, немного сложно вспомнить, что именно печатать, и немного сложно отменить его эффекты (нужно запустить :au! BufWritePost myfile.py). Кроме того, это решение блокирует Vim до завершения выполнения команды.

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

Чтобы отобразить вывод программы (и полностью прервать процесс редактирования, поскольку вывод будет записываться в ваш редактор в течение нескольких секунд, пока вы не нажмете Enter), удалите :silentкоманду.

Денилсон Са Майя
источник
1
Это может быть неплохо в сочетании с entr(см. Ниже) - просто заставьте vim коснуться фиктивного файла, который просматривает entr, и пусть entr сделает все остальное в фоновом режиме ... или, tmux send-keysесли вы оказались в такой среде :)
Пол Фенни
отлично! Вы можете сделать макрос для вашего .vimrcфайла
ErichBSchulz
23

Если у вас npmустановлена ​​программа, nodemonвозможно, это самый простой способ начать работу, особенно в OS X, которая, очевидно, не имеет инструментов inotify. Он поддерживает запуск команды при изменении папки.

davidtbernal
источник
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перечислит каждый файл и каталог с указанием размера и даты, поэтому, если файл будет изменен, он должен выйти из команды, как говорит man:

-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, только если изменяется какой-либо файл: watch -n1 "watch -d -t -g ls -lR && MY_COMMAND"
mnesarco
Моя версия watch (в 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 как 'rerun' в вашей переменной PATH и вызовите ее, используя:

rerun COMMAND

Он запускает COMMAND каждый раз, когда в вашем текущем каталоге происходит событие изменения файловой системы (рекурсивно.)

Вещи, которые могут понравиться об этом:

  • Он использует inotify, поэтому более отзывчив, чем опрос. Потрясающе для запуска юнит-тестов с точностью до миллисекунды или рендеринга файлов графических точек каждый раз, когда вы нажимаете «сохранить».
  • Поскольку это так быстро, вам не нужно беспокоиться о том, чтобы игнорировать большие подкаталоги (например, node_modules) только из соображений производительности.
  • Это очень супер-отзывчивый, потому что он вызывает inotifywait только один раз, при запуске, вместо того, чтобы запускать его, и на каждой итерации возникает дорогостоящий удар по созданию часов.
  • Это всего лишь 12 строк Bash
  • Поскольку это Bash, он интерпретирует команды, которые вы передаете, точно так же, как если бы вы вводили их в приглашении Bash. (Предположительно, это менее круто, если вы используете другую оболочку.)
  • Он не теряет события, которые происходят во время выполнения команды COMMAND, в отличие от большинства других решений inotify на этой странице.
  • При первом событии он вводит «мертвый период» на 0,15 секунды, в течение которого другие события игнорируются, прежде чем COMMAND будет запущен ровно один раз. Это происходит из-за того, что поток событий, вызванных танцем create-write-move, который Vi или Emacs делает при сохранении буфера, не вызывает много трудоемких выполнений, возможно, медленного набора тестов. Любые события, которые затем происходят во время выполнения COMMAND, не игнорируются - они вызовут второй мертвый период и последующее выполнение.

Вещи, которые могут не понравиться об этом:

  • Он использует inotify, поэтому не будет работать за пределами Linuxland.
  • Поскольку он использует inotify, он будет пытаться просмотреть каталоги, содержащие больше файлов, чем максимальное количество пользовательских наблюдений inotify. По умолчанию на разных компьютерах, которые я использую, установлено, что оно составляет от 5000 до 8000, но его легко увеличить. См. Https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Не удается выполнить команды, содержащие псевдонимы Bash. Я могу поклясться, что раньше это работало. В принципе, поскольку это Bash, который не выполняет COMMAND в подоболочке, я ожидал, что это сработает. Я хотел бы услышать, если кто-нибудь знает, почему это не так. Многие другие решения на этой странице также не могут выполнять такие команды.
  • Лично я хотел бы нажать клавишу в терминале, в котором он работает, чтобы вручную вызвать дополнительное выполнение команды. Могу ли я добавить это как-нибудь, просто? Параллельно работающий цикл while read -n1, который также вызывает execute?
  • Прямо сейчас я кодировал его, чтобы очистить терминал и печатать выполненную КОМАНДУ на каждой итерации. Некоторые люди могут захотеть добавить флаги командной строки, чтобы отключить подобные вещи и т. Д. Но это увеличит размер и сложность во много раз.

Это уточнение ответа @ cychoi.

Джонатан Хартли
источник
2
Я считаю, что вы должны использовать "$@"вместо $@, чтобы правильно работать с аргументами, содержащими пробелы. Но в то же время вы используете eval, что заставляет пользователя перезапуска быть особенно осторожным при цитировании.
Денилсон Са Майя
Спасибо Денилсон. Не могли бы вы привести пример, где цитирование должно быть сделано осторожно? Я использовал его последние 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'имеет немного другую семантику (расширение пути происходит только один раз или несколько раз).
Денилсон Са Майя
Спасибо за помощь. Да: rerun ls "some file"ломается из-за пробелов. rerun touch *.foo*обычно работает нормально, но не работает, если имена файлов, соответствующие * .foo, содержат пробелы. Спасибо за то, что помогли мне понять, какова rerun 'touch *.foo'различная семантика, но я подозреваю, что версия с одинарными кавычками - это семантика, которую я хочу: я хочу, чтобы каждая итерация повторения работала так, как будто я набирал команду снова - следовательно, я хочу *.foo расширяться на каждой итерации , Я попробую ваши предложения, чтобы изучить их последствия ...
Джонатан Хартли
Подробнее об этом пиаре ( github.com/tartley/rerun2/pull/1 ) и других.
Джонатан Хартли,
12

Вот простой сценарий оболочки Bourne, который:

  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), но в этом случае вы можете просто жестко закодировать имя временного файла.

MikeyMike
источник
9
Опрос является единственным переносимым способом, но большинство систем имеют механизм уведомления об изменении файла (inotify в Linux, kqueue во FreeBSD, ...). Когда вы это делаете $cmd, у вас возникает серьезная проблема с цитированием , но, к счастью, это легко исправить: отбросьте cmdпеременную и выполните "$@". Ваш скрипт не подходит для мониторинга большого файла, но это можно исправить, заменив cpна touch -r(вам нужна только дата, а не содержимое). Что касается переносимости, для -ntтеста требуется bash, ksh или zsh.
Жиль
8

Посмотрите на Incron . Это похоже на cron, но использует события inotify вместо времени.

Флориан Диш
источник
Это можно заставить работать, но создание записи incron довольно трудоемкий процесс по сравнению с другими решениями на этой странице.
Джонатан Хартли
6

Другое решение с NodeJs, fsmonitor :

  1. устанавливать

    sudo npm install -g fsmonitor
    
  2. Из командной строки (например, монитор журналов и «розничная торговля», если один файл журнала изменяется)

    fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
    
Атика
источник
Примечание: пример может быть решен tail -F -q *.log, я думаю.
Фолькер Сигел
Это было просто, чтобы привести пример, 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 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW с портами завершения ввода / вывода; рабочие потоки ReadDirectoryChangesW)
  • Независимо от ОС (опрос диска на наличие снимков каталогов и их периодическое сравнение; медленный и не рекомендуется)

Просто написал оболочку для командной строки watchdog_exec:

Пример работает

На событии fs, включающем файлы и папки в текущем каталоге, выполните echo $src $dstкоманду, если она не изменена, а затем выполните python $srcкоманду.

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

Использование коротких аргументов и ограничение на выполнение только тогда, когда в событиях используется « main .py»:

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

РЕДАКТИРОВАТЬ: Только что обнаружил, что Watchdog имеет официальный CLI называется watchmedo, так что проверьте это также.

Сэмюэл Маркс
источник
4

Если ваша программа генерирует какой-либо журнал / вывод, вы можете создать Makefile с правилом для этого журнала / вывода, который зависит от вашего сценария и сделать что-то вроде

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

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

ctgPi
источник
11
while sleep 1 ; do something ; doneнемного лучше чем while true ; do something ; sleep 1 ; done. По крайней мере, он легко останавливается при нажатии Ctrl + C.
Денилсон Са Майя
Приведет ли удаление в спящий режим к напряженной петле (процессор выделяет тепло и ухудшает время автономной работы ноутбука)?
Стивен Лу
2
@ StevenLu: нет, сон не занят. Проблема в том, что если сон находится в теле, Control-C прервет сон и цикл начнется заново. Потребляемая мощность при запуске цикла незначительна. Попробуйте сами в терминале. Вам нужно держать Control-C, чтобы он работал, если вы спите в организме.
Янус Троелсен
Правильно. Я думаю, что пропустил это и не видел, что сон все еще присутствует как условие петли. Этот маленький твик довольно классный.
Стивен Лу
4

swarminglogic написал скрипт под названием watchfile.sh , также доступный в виде GitHub Gist .

Денилсон Са Майя
источник
2
Это многофункциональный 200-строчный Bash-скрипт, который опрашивает 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
cychoi
источник
Это лучший ответ на странице только для пользователей Linux. Замените содержимое внутри цикла на «execute $ @», и пользователь может вызвать этот скрипт, передав свою собственную команду для запуска. Он даже работает с командами, которые содержат псевдонимы оболочки, если вы его используете, используя что-то вроде ". Scriptname COMMAND". Это все равно найдет имя скрипта в PATH.
Джонатан Хартли,
Я думаю, что вы хотите поставить «пока читаете ОТВЕТИТЬ»?
Джонатан Хартли,
1
Спасибо за разъяснения. Спасибо за поэтапность этого! Я бы удалил эти комментарии, но теперь, конечно, не буду.
Джонатан Хартли,
3

Еще немного о программировании, но вы хотите что-то вроде inotify . Существуют реализации на многих языках, таких как jnotify и pyinotify .

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

Джон Т
источник
3

Для тех из вас, кто ищет решение FreeBSD, вот порт:

/usr/ports/sysutils/wait_on
akond
источник
3

Мне нравится простота, while inotifywait ...; do ...; doneоднако у нее есть две проблемы:

  • Изменения файлов, происходящие во время, do ...;будут пропущены
  • Медленно при использовании в рекурсивном режиме

Для этого я создал вспомогательный скрипт, который использует inotifywait без этих ограничений: inotifyexec

Я предлагаю вам поставить этот скрипт на вашем пути, как в ~/bin/. Использование описывается просто запуском команды.

Пример: inotifyexec "echo test" -r .

Wernight
источник
Обновлен скрипт для поддержки сопоставления с регулярным выражением.
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вывода.

alex_1948511
источник
2

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

Reflex - небольшой инструмент для просмотра каталога и повторного запуска команды при изменении определенных файлов. Он отлично подходит для автоматического запуска задач компиляции / lint / test и для перезагрузки приложения при изменении кода.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make
masterxilo
источник
Можете ли вы процитировать / объяснить немного об инструменте? Быстро прочитайте, как рекомендовать программное обеспечение для руководства.
17
1

Ответ oneliner, который я использую, чтобы отслеживать изменения файла:

$ 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, чтобы отслеживать путь / файл на предмет изменений и что-то делать, когда это происходит. К вашему сведению - 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>
Hefewe1zen
источник
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/папку, чтобы запустить его при запуске.

LondonRob
источник
Разделяет проблему с ответом Джайлса о том, что он запускает inotifywait на каждой итерации, что может не отвечать на рекурсивный просмотр очень больших каталогов. Смотрите ответ cychoi для исправления этого.
Джонатан Хартли
0

Инструмент «fido» может быть еще одним вариантом для этой необходимости. Смотрите https://www.joedog.org/fido-home/

Дэвид Рамирес
источник
Пожалуйста, прочитайте, Как я рекомендую программное обеспечение для некоторых советов относительно того, как Вы должны рекомендовать программное обеспечение. Вы должны предоставить хотя бы ссылку, некоторую дополнительную информацию о самом программном обеспечении и о том, как его можно использовать для решения поставленной задачи.
DavidPostill
0

Как и некоторые другие, я также написал для этого облегченный инструмент командной строки. Он полностью документирован, протестирован и модульный.

Часы-Do

Монтаж

Вы можете установить его (если у вас есть 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флаг снова)
  • Динамически поддерживает список файлов для просмотра, если используется Globbing ( -rчтобы включить это)
  • Несколько способов «посмотреть» файл:
    • Время модификации (по умолчанию)
    • Файловый хеш
    • Тривиально реализовать свой собственный (это наблюдатель ModificationTime )
  • Модульная конструкция. Если вы хотите, чтобы команды выполнялись, когда к файлу обращаются, написать свой собственный наблюдатель (механизм, который определяет, должны ли исполнители выполняться), тривиально.
vimist
источник