2010-08-27 20:02:40 +0000 2010-08-27 20:02:40 +0000
459
459

ファイルが変更されるたびにコマンドを実行する方法は?

ファイルが変更されるたびにコマンドを実行するための迅速でシンプルな方法が欲しいです。私は非常にシンプルなものが欲しいのですが、ターミナル上で実行したままにしておいて、そのファイルでの作業が終了したらいつでもそれを閉じます。

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

または他の簡単なソリューションが欲しいのですが。さらに、プログラムの出力を見たい(エラーメッセージを見たい)ので、ターミナルで実行できるものが欲しいです。

回答について:ご回答ありがとうございました! どれもとても良いものばかりで、それぞれのアプローチの仕方が全く違います。1つだけ受け入れなければならないので、一番エレガントなものではないとわかっていても、実際に使ってみたもの(シンプルで早くて覚えやすかったもの)を受け入れています。

回答 (37)

434
434
434
2010-08-27 20:54:55 +0000

シンプルで、 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 が実行されている間) に実行された変更を見逃してしまいます。2 番目のスニペットにはこのような欠陥はありません。しかし、ファイル名に空白が含まれていないことを前提としていることに注意してください。もしそれが問題ならば、--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 (Linux の inotify のアナログを使用して、他の多くの Unix バリアントでも動作するツールです) のような、同じ基本的な機能を使用する別のツールを使用してください。

179
179
179
2013-10-25 09:41:16 +0000

(entr http://entrproject.org/ ) は inotify のよりフレンドリーなインターフェイスを提供します (そして *BSD & Mac OS X もサポートしています)。

複数のファイルを指定して監視するのを非常に簡単にしてくれます(ulimit -n によってのみ制限されます)。

-c (実行間に画面をクリアする) や -d (監視対象のディレクトリに新しいファイルが追加されたときに終了する) のようなフラグはさらに柔軟性を追加し、例えば次のようにすることができます:

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

2018年初頭の時点ではまだアクティブな開発中であり、Debian & Ubuntu (apt install entr) で見つけることができます; 作者のレポからのビルドはいずれにしても苦痛がありませんでした。

112
112
112
2011-06-30 13:34:28 +0000

使い方は簡単です:

when-changed FILE COMMAND...

または複数のファイルを見るために:

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

FILE はディレクトリになります。-r を使って再帰的にウォッチします。コマンドにファイル名を渡すには %f を使用します。

53
53
53
2013-08-20 17:12:47 +0000

このスクリプトはどうでしょうか。これは、statコマンドを使ってファイルのアクセス時間を取得し、アクセス時間に変化があったとき(ファイルにアクセスしたとき)にコマンドを実行します。

30
30
30
2010-08-27 20:12:25 +0000

Vim を使った解決策:

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

しかし、この解決策は入力するのがめんどくさいし、何を入力すればいいのか正確に覚えるのがめんどくさいし、効果を元に戻すのもめんどくさい(:au! BufWritePost myfile.py を実行する必要がある)ので、私は欲しくないです。さらに、このソリューションはコマンドの実行が終了するまで Vim をブロックします。

他の人の参考になるかもしれないので、完全性のためにここにこのソリューションを追加しました。

プログラムの出力を表示するには (Enter キーを押すまでの数秒間、出力がエディタの上に書き込まれるので、編集フローを完全に混乱させます)、:silent コマンドを削除してください。

23
23
23
2012-06-09 23:51:16 +0000

たまたま npm がインストールされている場合は、 nodemon を使うのが一番簡単な方法でしょう。フォルダが変更されたときのコマンド実行をサポートしています。

21
21
21
2016-03-22 14:57:03 +0000

私のように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}
17
17
17
2015-09-09 21:49:14 +0000

rerun2 on 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'として保存し、次のように起動します:

rerun COMMAND

カレントディレクトリ内でファイルシステムの変更イベントが発生するたびにCOMMANDを実行します(再帰的です。)。 )

  • inotify を使用しているので、ポーリングよりも反応が良いです。save’ を押すたびに、サブミリ秒単位のユニットテストを実行したり、 graphviz のドットファイルをレンダリングしたりするのに最適です。
  • とても速いので、わざわざ大きなサブディレクトリ(node_modules のような)を無視するように指示する必要はありません。
  • 起動時に一度だけ inotifywait を呼び出すだけなので非常に反応が良いです。
  • たった 12 行の Bash です
  • Bash なので、あなたが Bash プロンプトで入力したように正確にコマンドを解釈します。(おそらく他のシェルを使っている場合、これはあまりクールではありません。)
  • このページの他の多くの inotify ソリューションとは異なり、COMMAND 実行中に発生したイベントを失うことはありません。
  • 最初のイベントでは、COMMAND が正確に一度だけ実行される前に、0.15 秒間「デッドピリオド」に入ります。これは、バッファを保存する際にViやEmacsが行う作成・書き込み・移動のダンスによって引き起こされるイベントが、実行速度が遅くなる可能性のあるテストスイートを何度も実行しないようにするためです。COMMAND 実行中に発生したイベントは無視されません - 第二のデッドピリオドとそれに続く実行の原因になります。デフォルトでは、私が使用している異なるマシンでは、これは約5,000から8,000に設定されているようですが、増加させるのは簡単です。参照 https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Bash エイリアスを含むコマンドの実行に失敗します。以前はこれが機能していたと断言できます。原則として、これはBashであり、サブシェルでcommandを実行するのではないので、これが動作することを期待しています。なぜ動かないのか知っている人がいたら教えてほしいです。このページの他の解決策の多くはこのようなコマンドを実行できません。
  • 個人的には、実行中のターミナルでキーを押して手動で COMMAND を追加実行できたらと思います。どうにかして、簡単に追加できないでしょうか?同時に実行している'while read -n1'ループでexecuteも呼び出す?
  • 今のところ、ターミナルをクリアして、実行されたcommandを各イテレーションで表示するようにコード化しています。このようなものをオフにするためにコマンドラインフラグを追加したい人もいるかもしれません。

これは @cychoi さんの回答を改良したものです。

12
12
12
2010-08-27 21:23:29 +0000

ここに簡単なシェル・ボーン・シェル・スクリプトがあります:

  1. 2つの引数を取る: 監視するファイルとコマンド(必要に応じて引数付き)
  2. 監視するファイルを /tmp ディレクトリにコピーします。監視しているファイルがコピーより新しいかどうかを 2 秒ごとにチェックします
  3. 新しい場合はコピーを新しいオリジナルで上書きしてコマンドを実行します
  4. Ctr-C を押すと自分自身の後始末をします

これは FreeBSD で動作します。唯一の移植性の問題は、他の Unix に mktemp(1) コマンドがない場合ですが、その場合はテンポラリファイル名をハードコーディングするだけです。

8
8
8
2014-08-08 22:20:09 +0000

もし、 nodemon がインストールされている場合、あなたはこれを行うことができます:

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

私の場合、私はローカルでhtmlを編集し、ファイルが変更されたときに私のリモートサーバーにそれを出荷します。

8
8
8
2010-08-27 20:12:59 +0000
6
6
6
2014-07-09 13:16:10 +0000

特にこのプラグインでGuardを調べてみてください: https://github.com/hawx/guard-shell

あなたのプロジェクトのディレクトリ内の任意の数のパターンを監視し、変更が発生したときにコマンドを実行するように設定することができます。あなたが最初の場所で何をしようとしているそのために利用可能なプラグインがあることさえ良いチャンス。

6
6
6
2014-01-22 14:55:46 +0000

NodeJs を使ったもう一つの解決策は、* fsmonitor ** :

  1. インストール

  2. コマンドラインから (例:ログを監視して、1つのログファイルが変更された場合は “retail "とする)

5
5
5
2015-12-28 10:44:18 +0000
  • Python のプロジェクトであり、あなたが探しているものと同じかもしれません。 6 (inotify) > - Mac OS X (FSEvents, kqueue) > - FreeBSD/BSD (kqueue) > - Windows (ReadDirectoryChangesW with I/O completion ports; ReadDirectoryChangesW worker threads) > - OS に依存しない (ディレクトリスナップショットのためにディスクをポーリングし、定期的に比較する;遅いのでお勧めしません)

ただ、そのためのコマンドラインラッパーを書いただけです。

実行例

カレントディレクトリ内のファイルやフォルダを含む fs イベントでは、fs イベントが変更されていない限り、watchdog_exec コマンドを実行します。

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

短い引数を使用し、イベントに “ main.py” が含まれる場合のみ実行するように制限しています:

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

EDIT: ちょうど Watchdog には echo $src $dst という公式 CLI があることを発見しましたので、そちらもチェックしてみてください。

5
5
5
2012-07-02 16:36:51 +0000

Linux の場合:

man watch

watch -n 2 your_command_to_run

2 秒ごとにコマンドを実行します。

4
4
4
2010-08-27 20:37:52 +0000

プログラムが何らかのログ/出力を生成する場合、スクリプトに依存するログ/出力のルールを持つMakefileを作成して、

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

のようなことを行うことができます。

4
4
4
2014-09-15 23:24:58 +0000

swarminglogic ](http://swarminglogic.com/)は、[ watchfile.shというスクリプトを書きました。

4
4
4
2015-06-17 18:03:51 +0000

Gillesさんの回答](https://superuser.com/revisions/181543/3)を改良しました。

このバージョンでは、一度inotifywaitを実行して、その後イベント(例: 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
3
3
3
2010-08-27 20:05:59 +0000

もう少しプログラミング的な話になりますが、 inotify のようなものが欲しいと思います。jnotify ](http://jnotify.sourceforge.net/) や pyinotify など、多くの言語で実装されています。

このライブラリは、単一のファイルやディレクトリ全体を監視することができ、アクションが発見されるとイベントを返します。返される情報には、ファイル名、アクション(作成、変更、リネーム、削除)、ファイルパスなどの便利な情報が含まれます。

3
3
3
2012-11-11 21:48:31 +0000

FreeBSD のソリューションをお探しの方のために、以下のポートをご紹介します:

/usr/ports/sysutils/wait_on
3
3
3
2014-04-29 13:56:13 +0000

while inotifywait ...; do ...; doneのシンプルさが気に入っているのですが、2つの問題があります:

  • do ...;の実行中に起こったファイルの変更が見逃される
  • 再帰モードで使用する場合は遅い

そこで、これらの制限なしにinotifywaitを使用するヘルパースクリプトを作成しました。 inotifyexec

このスクリプトは、~/bin/のようにパスの中に入れておくことをお勧めします。使用方法はコマンドを実行するだけで説明します。

例: inotifyexec "echo test" -r .

3
3
3
2016-04-12 14:53:44 +0000

セバスチャンの解決策](https://superuser.com/a/1056022/493903)を`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

呼び出し例:

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

それは動作しますが、注意してください: watchコマンドには既知のバグがあります (manを参照) : それは-g CMD出力のターミナル部分のVISIBLEの変更のみに反応します。

2
2
2
2017-03-01 20:14:14 +0000

Reflex はディレクトリを監視し、特定のファイルが変更されたときにコマンドを再実行するための小さなツールです。コンパイル/リント/テストタスクを自動的に実行したり、コードが変更されたときにアプリケーションをリロードしたりするのに最適です。

1
1
1
2017-04-05 07:38:43 +0000

私はこれのためのGISTを持っていて、使用方法は非常に簡単です

watchfiles <cmd> <paths...>

https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55

1
1
1
2017-06-03 12:10:57 +0000

他の人がやっていたように、私もこれを行うための軽量なコマンドラインツールを書きました。これは完全に文書化され、テストされ、モジュール化されています。

Watch-Do

インストール

(Python3 と pip があれば) を使ってインストールできます:

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

使用法

002 すぐに実行して使ってください。

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

機能概要

  • ファイルのグロブをサポート (-w '*.py' または -w '**/*.py' を使用)
  • ファイルの変更に対して複数のコマンドを実行 (-d フラグを再度指定するだけ)
  • グロブが使用されている場合、監視するファイルのリストを動的に保持 (-r を使用してこれを有効にする)
  • 複数の方法でファイルを監視することができます。
  • ファイルの変更時刻(デフォルト)
  • ファイルハッシュ
  • 独自の実装が簡単にできます(これは ModificationTime ウォッチャーです)
  • モジュラー設計です。ファイルにアクセスしたときにコマンドを実行させたい場合は、ウォッチャー(実行すべきかどうかを判断する仕組み)を自作するのが手っ取り早い。
1
1
1
2016-02-26 19:10:05 +0000

私はそれを行うためにこのスクリプトを使用しています。I’m using inotify in monitor-mode

#!/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
1
1
2015-09-09 19:36:04 +0000

私は、 rerun と呼ばれる、まさにこれを行うためのPythonプログラムを書きました。

UPDATE: この回答は、変更をポーリングするPythonスクリプトであり、これはいくつかの状況で有用です。inotifyを使用するLinux専用のBashスクリプトについては、私の他の回答を参照してください、このページで'rerun2'を検索してください。したがって、追加しなければならない余分なエスケープを減らすために、示されているようにそれを引用します。

デフォルトでは、カレントディレクトリ以下のすべてのファイルを監視し、既知のソースコントロールディレクトリ、.git、.svn などをスキップします。これは複数回与えることができます。

Pythonスクリプトなので、コマンドをサブプロセスとして実行する必要があり、'COMMAND'を解釈して実際に実行するプロセスを決定するために、ユーザーの現在のシェルの新しいインスタンスを使用しています。しかし、コマンドに.bashrcで定義されているシェルエイリアスなどが含まれている場合、これらはサブシェルによって読み込まれません。これを修正するには、再実行時に ‘-I’ フラグを指定して、対話型 (別名 ‘ログイン’ サブシェル) サブシェルを使用することができます。これは .bashrc をソースにしなければならないので、通常のシェルを起動するよりも遅く、エラーが発生しやすいです。

私は Python 3 で使用していますが、最後に確認したところ、再実行は Python 2 でも動作していました。良い面では、これはそれがすべてのOSで動作することを意味します。

欠点としては、ポーリングは0.0~1.0秒の待ち時間があることを意味し、もちろん非常に大きなディレクトリを監視するのは遅いです。とはいえ、virtualenvやnode_modulesのような大きなものを無視するために’-i'を使っている限り、これが目立ってしまうほど大きなプロジェクトには遭遇したことがありません。しかし、ここに来てこれをタイプするようになって、inotify を使ったソリューションに切り替える必要があることは明らかです(私はもう Windows も OSX も使っていません)、Bash で書かれたソリューションに切り替える必要があります(だから余計な手を加えなくてもエイリアスで動作します)。

1
1
1
2015-09-01 04:53:52 +0000

ファイル変更の追跡に使っているオネリ回答:

$ 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
1
1
2016-03-22 15:33:56 +0000

OS Xを使っている人は、LaunchAgentを使ってパスやファイルの変更を監視し、変更が起こったときに何かをすることができます。参考までに、LaunchControlは簡単にデーモンやエージェントを作ったり、変更したり、削除したりするのに良いアプリです。

0
0
0
2019-11-26 15:53:22 +0000

このために新しいものをインストールしたくない場合は、パスに入れることができる小さなシェルスクリプトがあります (例: $HOME/bin の下)。これは、提供された一つ以上のファイルが変更されたときにコマンドを実行します。例えば:

$ onchange './build' *.txt
#!/bin/sh
cmd="$1"; shift
files="$@"
changed() { tar -c $files | md5sum; } # for on every save use: `stat -c %Z $files`
while true; do
  if ["$(changed)" != "$last"]; then
    last="$(changed)"
    $cmd
  fi
  sleep 1
done
$ onchange "!!" *.txt

それは、それがタールし、ファイルおよび/またはディレクトリの内容をハッシュします、従って、それはあなたが強制的にCTRL-Sを打つたびに実行されませんが(またはタイプ:w)、何かが実際に変更されたときにだけ実行されます。1秒ごとにチェックするので、あまり多く含まないようにしてください。保存ごとに実行させたい場合は、代わりに stat を使用してください (コメントを参照)。また、私の記憶が正しければ、mac では md5summd5 と呼ばれています。

ちょっとしたトリック: これを使いたいと思った瞬間に、最後に実行したコマンドを何度も何度も繰り返したくなるでしょう。このコマンドに最後のコマンドを「注入」するには !! ショートカットを使うことができます:

&001

0
0
0
2018-04-12 18:32:28 +0000

説明

これは、ファイルの変更を監視し、2 番目のステートメントとして ** が与えられたコマンドを実行します。注意: 各whileループサイクルの後に関数がスリープする秒数を変更することで、関数をより多く(またはより少ない)反応的にすることができます。

使用例

watch_file my_file.php php my_file.php

この行は、phpファイルmy_file.phpを監視し、それが変更されるたびにphpインタプリタを介して実行します。

0
0
0
2018-03-19 20:30:21 +0000
tail -q --follow=name myfile.txt | head -n 0

このコマンドは以下の条件で終了します:

  • コマンド実行後に myfile.txt に行が追加されます
  • コマンド実行後に myfile.txt が別のものに置き換えられます

あなたは vim を使用していると言っていますが、保存時に vim がファイルを置き換えてくれます。私はこれがvimで動作することをテストしました。

あなたはこのコマンドの出力を無視することができます、それは次のような何かを言及することがあります:

tail. このコマンドの出力は無視できますが、次のようなことが書かれているかもしれません。以下のように使えます:

timeout 5s bash -c ‘tail -q –follow=name pipe 2> /dev/null | head -n 0’ && echo changed || echo timeout

議論

timeout は、tail を裏で使っています。これが、ポーリングを行わずに非同期で動作させる方法です。

時々、これらのコマンドはすぐに終了してしまいますが、すぐに2回目の実行をすれば、広告通りに動作します。

RHEL 上では次のように使えます:

timeout 5s sh -c ‘geo monitor pipe | head -n 0’ && echo changed || echo timeout

しかし、これが移植性があるかどうかはわかりません。

0
0
0
2019-01-28 16:51:42 +0000

findはトリックを行うことができます。

while true; do
    find /path/to/watched/file -ctime 1s | xargs do-what-you-will
done

find -ctime 1sは、最後の1schangedされた場合にファイル名を印刷します。

0
0
0
2015-05-13 15:21:33 +0000

特定のファイルへの変更をググって見つけた人のために、答えはもっと簡単です( Gillesの答え に触発されて)。

特定のファイルに書き込まれた後に何かをしたい場合は、次のようにします:

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/フォルダに入れて、起動時に実行させます。

0
0
0
2019-03-21 11:33:37 +0000

私は少し違った状況を持っていました。しかし、この質問を読んでいる人には役に立つかもしれないと感じています。

ログファイルのサイズが変わったときに通知を受ける必要がありましたが、すぐに必要なわけではありませんでした。そして、それは数日先、数週間先のことになりかねないので、コマンドラインでinotify(どうせそのサーバーにはインストールされていない/起動されていない)を使うことができませんでした(nohupなどは使いたくありませんでした)。そこで、私はcron上でbashスクリプトを実行して

をチェックすることにしました。 スクリプトは、テキストファイルに監視しているファイルのファイルサイズを書き込み、cronを実行するたびに、その値が変更されているかどうかをチェックし、変更された場合は、最後の行を私にメールで送ってくれます。

0
0
0
2019-11-23 23:41:53 +0000

https://github.com/watchexec/watchexec ](https://github.com/watchexec/watchexec) をチェックアウトしてください。

watchexec は、パスを監視し、変更を検出したときにコマンドを実行するシンプルなスタンドアロンのツールです。

カレントディレクトリ内のすべての JavaScript、CSS、HTML ファイルを監視し、変更を検出したときに make を実行します。

0
0
0
2017-03-20 13:52:53 +0000

fido’ ツールは、この必要性のためのまだ別のオプションかもしれません。参照してください https://www.joedog.org/fido-home/