ファイルが変更されたときにコマンドを実行するための、迅速でシンプルな方法が欲しい。非常にシンプルで、ターミナル上で実行したままにしておいて、そのファイルの作業が終わったらいつでもそれを閉じられるようなものが欲しいのです
現在はこれを使っています
while read; do ./myfile.py ; done
そして、そのファイルをエディタで保存するときには、そのターミナルに行って、Enterを押す必要があります。私が欲しいのは、次のようなものです
while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done
あるいは、それと同じくらい簡単な他の解決策も
BTW: 私はVimを使っていて、BufWrite上で何かを実行するためのオートコマンドを追加できることは知っていますが、これは私が今望んでいるような解決策ではありません
更新:できれば捨てられるシンプルなものが欲しい。さらに、プログラムの出力が見たい(エラーメッセージが見たい)ので、ターミナルで実行できるものが欲しい
回答について。どの回答もありがとうございました!どれもとても良いものばかりで、それぞれのアプローチの仕方が全く違います。私は1つだけ受け止めなければならないので、一番上品ではないとわかっていても、実際に使ってみたもの(シンプルで早くて覚えやすかった)を受け止めています
497 None 2010-08-27
inotifywait (ディストリビューションのinotify-tools
パッケージをインストールする) を使って、シンプルに
while inotifywait -e close_write myfile.py; do ./myfile.py; done
or
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
に書き込むのではなく、myfile.py
を別のファイルに置き換えた場合、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 の各バリアントのアナログを使っています) のような、同じ基本的な機能を使っている別のツールを使ってもよいでしょう
468 Gilles ‘SO- stop being evil’ 2010-08-27
entr (http://entrproject.org/) は inotify のよりフレンドリーなインターフェイスを提供します (また *BSD & Mac OS X もサポートしています)
これにより、監視する複数のファイルを非常に簡単に指定することができます (ulimit -n
のみに制限されます)
$ 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
); 作者のレポからのビルドはいずれにしても痛みを伴うことはありませんでした
203 Paul Fenney 2013-10-25
私はwhen-changedというPythonプログラムを書きました
使い方は簡単です
when-changed FILE COMMAND...
複数のファイルを見ることも
when-changed FILE [FILE ...] -c COMMAND
FILE
にはディレクトリを指定することができます。-r
で再帰的にウォッチする。コマンドにファイル名を渡すには %f
を使う
122 joh 2011-06-30
このスクリプトはどうでしょうか。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
57 VDR 2013-08-20
Vimを使ったソリューション
:au BufWritePost myfile.py :silent !./myfile.py
しかし、この解決策は、入力するのがめんどくさいし、何を入力すればいいのか正確に覚えるのがちょっと大変だし、その効果を元に戻すのがちょっと難しい(:au! BufWritePost myfile.py
を実行する必要がある)ので、私はこの解決策を望んでいません。さらに、この解決策はコマンドの実行が終了するまで Vim をブロックします
他の人の助けになるかもしれないので、この解決策をここに追加しました
プログラムの出力を表示するには(Enterを押すまでの数秒間、出力がエディタの上に書き込まれるので、編集の流れを完全に混乱させます)、:silent
コマンドを削除してください
31 Denilson Sá Maia 2010-08-27
たまたま npm
がインストールされているのであれば、nodemon
を使うのが一番簡単な方法でしょう。フォルダが変更されたときのコマンド実行をサポートしています
26 davidtbernal 2012-06-09
私のように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}
26 Sebastian 2016-03-22
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 のバージョンを ‘rerun’ として PATH に保存し、以下のようにして起動します
rerun COMMAND
カレントディレクトリ内でファイルシステムの変更イベントが発生するたびにcommandを実行します(再帰的に)
気に入るかもしれないこと
- inotify を使用しているので、ポーリングよりも反応が良いです。サブミリ秒単位のユニットテストを実行したり、’save’ を押すたびに graphviz ドットファイルをレンダリングしたりするのに最適です
- 非常に高速なので、パフォーマンスの理由だけで大きなサブディレクトリ(node_modulesのような)を無視するようにわざわざ指示する必要はありません
- 起動時に一度だけ inotifywait を呼び出すだけなので、起動時の反応が非常に良く、繰り返しのたびに時計を確立するための高価なヒットが発生することはありません
- バッシュの12行だけです
- Bash なので、あなたが渡したコマンドを Bash プロンプトで入力したかのように正確に解釈します。(おそらく他のシェルを使っている場合は、これはあまりクールではないでしょう)
- このページの他の多くのinotifyソリューションとは異なり、COMMAND実行中に発生するイベントを失うことはありません
- 最初のイベントでは、0.15 秒間の「デッドピリオド」に入り、その間は他のイベントは無視されます。これは、バッファを保存する際にViやEmacsが行う「作成・書き込み・移動」のダンスによって引き起こされるイベントが、実行速度が遅くなる可能性のあるテストスイートを何度も実行しないようにするためです。COMMAND の実行中に発生したイベントは無視されません
嫌いになりそうなこと
- inotify を使用しているため、Linuxland 以外では動作しません
- inotify を使用しているため、inotify が監視するユーザーの最大数以上のファイルを含むディレクトリを監視しようとすると嘔吐します。デフォルトでは、私が使用している異なるマシンでは 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さんのアンサーを洗練させたものです
20 Jonathan Hartley 2015-09-09
ここに簡単なシェル・ボーンのシェル・スクリプトがあります
- 2つの引数を取ります: 監視するファイルとコマンド (必要に応じて引数付き)
- 監視しているファイルを/tmpディレクトリにコピーします
- 監視しているファイルがコピーより新しいかどうかを2秒ごとにチェックします
- 新しいものであれば、コピーを新しいオリジナルで上書きしてコマンドを実行します
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) コマンドを持っていない場合ですが、その場合はテンポラリファイル名をハードコードするだけです
13 MikeyMike 2010-08-27
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"
10 Jay 2014-08-08
incronを見てみてください。これはcronに似ていますが、時間の代わりにinotifyイベントを使用します
9 Florian Diesch 2010-08-27
NodeJsを使ったもう一つの解決策は、fsmonitor
Install
sudo npm install -g fsmonitor
コマンドラインから(例:ログを監視し、1つのログファイルが変更された場合は「小売」)
fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
8 Atika 2014-01-22
特にこのプラグインでGuardを調べてみてください
あなたのプロジェクトのディレクトリ内の任意の数のパターンを監視し、変更が発生したときにコマンドを実行するように設定することができます。そもそもあなたがやろうとしていることに対応したプラグインが用意されている可能性も十分にあります
7 Wouter Van Vliet 2014-07-09
Under Linux:
man watch
watch -n 2 your_command_to_run
2秒ごとにコマンドを実行します
コマンドを実行するのに2秒以上かかる場合、ウォッチは実行が終わるまで待ってから再度実行します
6 Eric Leschinski 2012-07-02
WatchdogはPythonのプロジェクトで、あなたが探しているものと同じかもしれません
Supported platforms
- Linux 2.6 (inotify)
- Mac OS X(FSEvents、kqueue)
- FreeBSD/BSD (kqueue)
- Windows(I/O補完ポートを持つReadDirectoryChangesW; ReadDirectoryChangesWワーカースレッド)
- OS に依存しない (ディレクトリのスナップショットをディスクにポーリングし、定期的に比較します。)
そのためのコマンドラインラッパー watchdog_exec
を書いただけです
Example runs
カレントディレクトリ内のファイルやフォルダを含むfsイベントでは、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
EDIT: Watchdogにはwatchmedo
という公式のCLIがあることを発見したので、そちらもチェックしてみてください
6 Samuel Marks 2015-12-28
プログラムが何らかのログ/出力を生成している場合は、スクリプトに依存するログ/出力のルールを持つMakefileを作成して、次のようにします
while true; do make -s my_target; sleep 1; done
別の方法として、偽のターゲットを作成して、そのルールでスクリプトを呼び出し、偽のターゲットに触れるようにすることもできます (スクリプトに依存したまま)
5 ctgPi 2010-08-27
swarminglogic は watchfile.sh というスクリプトを書き、GitHub Gist としても利用可能です
5 Denilson Sá Maia 2014-09-15
ジルの回答を改良しました
このバージョンでは、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
5 cychoi 2015-06-17
もう少しプログラミング側の話になりますが、inotifyのようなものが欲しくなります。jnotify や pyinotify など、多くの言語で実装されています
このライブラリを使用すると、単一のファイルまたはディレクトリ全体を監視することができ、アクションが発見されるとイベントを返します。返される情報には、ファイル名、アクション (作成、変更、リネーム、削除)、ファイルパスなどの有用な情報が含まれます
4 John T 2010-08-27
FreeBSDをお探しの方のために、こちらの移植版をご紹介します
/usr/ports/sysutils/wait_on
4 akond 2012-11-11
私はwhile inotifywait ...; do ...; done
のシンプルさが好きですが、2つの問題があります
do ...;
の間に起こったファイルの変更は見逃されます- 再帰モードで使用すると遅い
そこで、これらの制約を受けずにinotifywaitを利用するヘルパースクリプトを作ってみました。 inotifyexec です
このスクリプトを~/bin/
のようにパスに入れておくといいと思います。使い方はコマンドを実行するだけで説明します
例としては、以下のようになります。inotifyexec "echo test" -r .
4 Wernight 2014-04-29
セバスチャンの解決策を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
Call example:
watch_cmd.sh "ls -lR /etc/nginx | grep .conf$" "sudo service nginx reload"
動作しますが、注意が必要です。watch
コマンドには既知のバグがあります (man を参照してください): -g CMD
出力のターミナル部分の VISIBLE の変更のみに反応します
4 alex_1948511 2016-04-12
あなたはreflexを試してみてください
Reflex はディレクトリを監視し、特定のファイルが変更されたときにコマンドを再実行するための小さなツールです。コンパイル/リント/テストタスクを自動的に実行したり、コードが変更されたときにアプリケーションをリロードしたりするのに最適です
# Rerun make whenever a .c file changes
reflex -r '\.c$' make
3 masterxilo 2017-03-01
ファイル変更の記録を残すために使っているオネリ回答
$ 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
のデバッグと監視に使用しています
2 DrBeco 2015-09-01
私はこのスクリプトを使っています。モニターモードでイノティファイを使っています
#!/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 を実行します
2 Fernando Silva 2016-02-26
OS Xを使っている人は、LaunchAgentを使ってパスやファイルの変更を監視し、変更が起こったときに何かをすることができます。参考までに – LaunchControlは簡単にデーモンやエージェントを作成、変更、削除するのに良いアプリです
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
Page Not Found - AppleApple info, Mac, iPhone, iPad, Apple Watch, and more. Find your way around apple.com.
<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>
2 Hefewe1zen 2016-03-22
これのためにGISTを持っているのですが、使い方はかなり簡単です
watchfiles <cmd> <paths...>
2 thiagoh 2017-04-05
他の人がやっていたように、私もこれを行うための軽量なコマンドラインツールを書きました。これは完全に文書化され、テストされ、モジュール化されています
Installation
を使ってインストールします(Python3とpipがあれば)
pip3 install git+https://github.com/vimist/watch-do
Usage
走ってすぐに使う
watch-do -w my_file -d 'echo %f changed'
Features Overview
- ファイルのグロブをサポートしています(
-w '*.py'
または-w '**/*.py'
を使用) - ファイルの変更に対して複数のコマンドを実行する(
-d
フラグを再度指定するだけ) - グロブが使用されている場合、監視するファイルのリストを動的に保持する (
-r
でオンにする) - ファイルを「見る」には複数の方法があります:
- 変更時間(デフォルト)
- File hash
- 自分で実装するのは簡単です(これはModificationTimeウォッチャーです)
- モジュラー設計。ファイルにアクセスしたときに、コマンドを実行させたいのであれば、自分でウォッチャー(実行すべきかどうかを判断する仕組み)を書くのは些細なことです
2 vimist 2017-06-03
まさにこれを行うためのPythonのプログラムを書いたのがrerun
です
UPDATE: この回答は、変更をポーリングするPythonスクリプトで、状況によっては便利です。inotifyを使用するLinux専用のBashスクリプトについては、私の別の回答を参照してください
Python2、Python3用にインストールします
pip install --user rerun
と使い方はとてもシンプルです
rerun "COMMAND"
コマンドは、スペースで区切られた一連の引数ではなく、単一の引数として期待されています。そのため、表示されているように引用符で囲んでおくことで、追加しなければならない余分なエスケープを減らすことができます。コマンドラインでコマンドを入力するときと同じように、引用符で囲んで入力してください
デフォルトでは、カレントディレクトリ以下のすべてのファイルを監視し、既知のソース管理用ディレクトリ、.git、.svn などのようなものはスキップします
オプションのフラグとして、名前付きファイルやディレクトリへの変更を無視する ‘-i NAME’ があります。これは複数回指定することができます
これはPythonスクリプトなので、コマンドをサブプロセスとして実行する必要があり、ユーザーの現在のシェルの新しいインスタンスを使用して’COMMAND’を解釈し、実際に実行するプロセスを決定します。しかし、コマンドに.bashrcで定義されているシェルエイリアスなどが含まれている場合、これらはサブシェルによって読み込まれません。これを修正するには、再実行時に ‘-I’ フラグを指定して、対話型 (別名 ‘ログイン’ サブシェル) サブシェルを使用することができます。この方法は、通常のシェルを起動するよりも遅く、エラーが発生しやすいです
私はPython 3で使っていますが、最後に確認したところ、rerunはまだPython 2で動作していました
両刃の剣は、イノティファイの代わりにポーリングを使用していることです。良い面としては、これはすべての OS で動作することを意味します。さらに、ここで示した他のいくつかのソリューションよりも、変更されたファイルごとに一度だけではなく、ファイルシステムの変更の束に対して一度だけコマンドを実行するという点で優れています
欠点としては、ポーリングは 0.0~1.0 秒のレイテンシがあることを意味し、もちろん、非常に大きなディレクトリを監視するのは遅いです。とはいえ、virtualenv や node_modules のような大きなものを無視するために ‘-i’ を使用する限り、これが目立ってしまうほど大きなプロジェクトに遭遇したことはありません
うーん。rerun
は何年も前から私にとってなくてはならないもので、基本的に毎日8時間はテストをしたり、ドットファイルを編集しながら再構築したりしています。しかし、今ここにこれを書きに来て、私は inotify を使ったソリューションに切り替える必要があることは明らかです(私はもう Windows や OSX は使っていません)
2 Jonathan Hartley 2015-09-09
特定のファイルへの変更をググって見つけた人にとっては、答えはもっとシンプルです(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/
フォルダに入れて起動時に実行させます
1 LondonRob 2015-05-13