ファイルが変更されたときにコマンドを実行するための迅速で簡単な方法が欲しいのです。非常に単純なもの、つまり端末上で実行したままにしておき、そのファイルの処理が終わったらいつでも閉じたいものが欲しいのです。
現在、私はこれを使っています:
while read; do ./myfile.py ; done
それから私はそのターミナルに行き、押す必要があります Enter私のエディタでそのファイルを保存するときはいつでも。私が欲しいのはこのようなものです。
while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done
またはそれと同じくらい簡単な他の解決策。
ところで:私はVimを使用しています、そしてBufWrite上で何かを実行するためにautocommandを追加できることは知っていますが、これは私が今望んでいるような解決策ではありません。
更新:シンプルで、できれば破棄できるものが欲しいのですが。さらに、プログラムの出力を見たいので(エラーメッセージを見たいのですが)、端末で何かを実行したいのです。
回答について:あなたのすべての答えてくれてありがとう!それらはすべて非常に優れており、それぞれが他のものとは非常に異なるアプローチを取ります。私は1つだけを受け入れる必要があるので、私はそれが最もエレガントではないことを知っていても、私は実際に使ったものを受け入れています(それは単純で、素早く、覚えやすいものでした)。
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
に書き込むのではなく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 のような基本的な機能を使用する別のツールを使用します。 Linuxのinotifyの各変種のアナログを使用して、他の多くのUnixの変種でも動作するツール。
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
)にあります。いずれにせよ、著者のレポから構築するのは苦労しませんでした。
私はこれを正確に行うためにPythonプログラムを書いて、 when-changed と呼びました。
使い方は簡単です。
when-changed FILE COMMAND...
複数のファイルを見るには
when-changed FILE [FILE ...] -c COMMAND
FILE
はディレクトリにすることができます。 -r
を使って再帰的に監視します。ファイル名をコマンドに渡すには、%f
を使用します。
このスクリプトはどうですか?ファイルのアクセス時間を取得するために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
Vimを使った解決策:
:au BufWritePost myfile.py :silent !./myfile.py
しかし、私はこの解決策を望みません。なぜなら、それはタイプするのがちょっと面倒で、タイプするものを正確に覚えるのは少し難しいし、その効果を元に戻すのは少し難しいからです(:au! BufWritePost myfile.py
を実行する必要があります)。さらに、このソリューションはコマンドが実行を終了するまでVimをブロックします。
他の人に役立つかもしれないので、私は完全を期してこのソリューションをここに追加しました。
プログラムの出力を表示するには(Enterキーを押すまで出力がエディタに数秒間上書きされるため、編集フローを完全に中断します)、:silent
コマンドを削除します。
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を実行します(再帰的)。
それについて好むかもしれないもの:
嫌いなことがあるかもしれません。
これは@ cychoiの回答の改良です。
私のように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}
これは、単純なShell Bourne Shellスクリプトです。
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)コマンドを持っていない場合ですが、その場合は一時ファイル名をハードコードすることができます。
incron をご覧ください。これはcronに似ていますが、時間の代わりにinotifyイベントを使用します。
NodeJを使った別の解決法は、fsmonitorです。
インストール
Sudo npm install -g fsmonitor
コマンドラインから(例:ログを監視し、1つのログファイルが変更された場合は「製品版」)
fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
特にこのプラグインで、Guardを調べてください。
https://github.com/hawx/guard-Shell
プロジェクトのディレクトリにあるパターンをいくつでも監視し、変更があったときにコマンドを実行するように設定できます。そもそもあなたがやろうとしていることのために利用可能なプラグインがあるという可能性も十分にあります。
Linuxの場合
man watch
watch -n 2 your_command_to_run
2秒ごとにコマンドを実行します。
コマンドの実行に2秒以上かかる場合、watchはそれが完了するまで待ってから再度実行します。
nodemon がインストールされている場合は、次のようにします。
nodemon -w <watch directory> -x "<Shell command>" -e ".html"
私の場合は、HTMLをローカルで編集し、ファイルが変更されたときにそれをリモートサーバーに送信します。
nodemon -w <watch directory> -x "scp filename [email protected]:/var/www" -e ".html"
ウォッチドッグはPythonプロジェクトです。
対応プラットフォーム
- Linux 2.6(inotify)
- Mac OS X(FSEvents、kqueue)
- FreeBSD/BSD(kqueue)
- Windows(I/O完了ポートを持つReadDirectoryChangesW、ReadDirectoryChangesWワーカースレッド)
- OSに依存しない(ディレクトリのスナップショットを探すためにディスクをポーリングし、それらを定期的に比較する。遅くて推奨されない)
そのためのコマンドラインラッパーを書いただけですwatchdog_exec
:
現在のディレクトリ内のファイルやフォルダを含む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
編集:Watchdogにはwatchmedo
という正式なCLIがあることがわかったので、それも調べてください。
あなたのプログラムがある種のログ/出力を生成するなら、あなたはあなたのスクリプトに依存するそのログ/出力のための規則でMakefileを作成することができ、そして以下のようなことをすることができます。
while true; do make -s my_target; sleep 1; done
別の方法として、偽のターゲットを作成し、それに対するルールでスクリプトを呼び出して偽のターゲットに触れることもできます(それでもスクリプトに依存します)。
Gillesの答え を改善しました。
このバージョンは一度inotifywait
を実行し、その後イベント(.e.g .: 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
swarminglogic は、 watchfile.sh というスクリプトを書き、 GitHub Gist として入手可能です。
私はwhile inotifywait ...; do ...; done
の単純さが好きですが、それには2つの問題があります。
do ...;
の間に行われたファイルの変更は見逃されますそのため、制限なしでinotifywaitを使用するヘルパースクリプトを作成しました。 inotifyexec
~/bin/
のように、このスクリプトを自分のパスに入れることをお勧めします。使い方はコマンドを実行するだけで説明されます。
例:inotifyexec "echo test" -r .
watch
コマンドを使用して Sebastianの解決策 を改善しました。
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を参照):-g CMD
出力の末尾部分のVISIBLEの変更にのみ反応します。
FreeBSDのソリューションを探している人のために、ここにportがあります。
/usr/ports/sysutils/wait_on
あなたは 反射 を試すことができます。
Reflexは、ディレクトリを監視し、特定のファイルが変更されたときにコマンドを再実行するための小さなツールです。コンパイル/ lint/testタスクを自動的に実行したり、コードが変更されたときにアプリケーションをリロードしたりするのに最適です。
# Rerun make whenever a .c file changes
reflex -r '\.c$' make
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>
私はこのための要旨を持っています、そして使い方はとても簡単です。
watchfiles <cmd> <paths...>
https://Gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55
私はそれをするのにこのスクリプトを使います。 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が実行されます。
私がファイルの変更を追跡するために使っているのは厄介な答えです。
$ 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
を失い続けること
これ以上のソフトウェアをインストールする必要がなく、箱から出して動作するソリューションです。
tail -q --follow=name myfile.txt | head -n 0
このコマンドは以下の条件で終了します。
myfile.txt
に1行追加されます。myfile.txt
は、コマンドの実行後に別のものに置き換えられますあなたはあなたがvimを使っていると言います、そしてvimは保存時にファイルを置き換えます。私はこれをvimでテストしました。
このコマンドの出力は無視してかまいません。
tail: 'myfile.txt'は置き換えられました。新しいファイルの末尾に続く
これをtimeout
と組み合わせてtrueまたはfalseを返すことができます。あなたはこのようにそれを使うことができます:
タイムアウト5秒bash -c 'tail -q --follow =名前パイプ2>/dev/null | head -n 0 '&& echoが変更されました||エコータイムアウト
tail
はフードの下でinotify
を使用します。これが、あなたがポーリングなしでこの空想的な非同期の振る舞いをする方法です。おそらく他のinotify
を使った標準的なunixプログラムがあります。
これらのコマンドはすぐに終了することがありますが、2度目に実行した場合は、アドバタイズされたとおりに機能します。どこかに1つずつエラーを出しました。これを修正してください。
RHEL上で私は使用することができます:
タイムアウト5秒sh -c 'gioモニターパイプhead -n 0 '&& echoが変更されました||エコータイムアウト
しかし、それが移植可能かどうかはわかりません。
find
がトリックをやることができます。
while true; do
find /path/to/watched/file -ctime 1s | xargs do-what-you-will
done
最後のfind -ctime 1s
で変更された場合、1s
はファイル名を表示します。
'fido'ツールは、この必要性のためのさらに別の選択肢かもしれません。 https://www.joedog.org/fido-home/ を参照してください。
特定の ファイルへの変更のためにグーグルによってこれを見つける人のために、答えははるかに簡単です( ジルの答え に触発されて)。
もしあなたが何かをしたいのであれば - /特定のファイルに書き込まれた後、これは次の通りです:
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/
フォルダーに入れて、起動時に実行できるようにします。
他にもいくつか行ったように、これを行うための軽量のコマンドラインツールも書いています。完全に文書化され、テストされ、そしてモジュール化されています。
あなたはそれをインストールすることができます(あなたが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
)私は rerun
と呼ばれる、これを正確に行うためのPythonプログラムを書きました。
更新:この答えは変更をポーリングするPythonスクリプトです。これは状況によっては便利です。 inotifyを使用するLinux専用のBashスクリプトについては、私の他の答えを見て、このページで 'rerun2'を検索してください。
Python 2またはPython 3にインストールするには、
pip install --user rerun
使い方はとても簡単です。
rerun "COMMAND"
このコマンドは、空白で区切られた一連の引数ではなく、単一の引数として想定されています。したがって、示されているとおりに引用してください。これにより、追加する必要のある余分なエスケープが減少します。コマンドラインで入力したのと同じように、引用符で囲んでコマンドを入力します。
デフォルトでは、カレントディレクトリ内またはその下にあるすべてのファイルを監視し、既知のソース管理ディレクトリ、.git、.svnなどをスキップします。
オプションのフラグには、名前付きファイルまたはディレクトリへの変更を無視する '-i NAME'が含まれます。これは複数回指定できます。
これはPythonスクリプトなので、コマンドをサブプロセスとして実行する必要があります。ユーザーの現在のシェルの新しいインスタンスを使用して「COMMAND」を解釈し、実際に実行するプロセスを決定します。ただし、コマンドに.bashrcで定義されているシェルエイリアスなどが含まれている場合、これらはサブシェルによって読み込まれません。これを修正するために、対話的な(別名 'login')サブシェルを使うために '-I'フラグを再実行することができます。通常のシェルを起動するよりも遅く、エラーが発生しやすくなります。なぜなら、それはあなたの.bashrcを読み込む必要があるからです。
私はPython 3でそれを使っていますが、最後に私がチェックしたのはPython 2でも動いています。
両刃の刀は、それがinotifyの代わりに投票を使うということです。逆に言えば、これはすべてのOSで動作するということです。加えて、変更されたファイルごとに一度だけではなく、ファイルシステムの変更に対して一度だけ指定されたコマンドを実行するという点で、ここに示された他の解決策より優れています。コマンド実行中.
不利な点として、ポーリングは0.0から1.0秒の待ち時間があることを意味し、もちろん非常に大きなディレクトリを監視するのは遅いです。とは言っても、virtualenvやnode_modulesのような大きなものを無視するために '-i'を使用する限り、これが目立つほど大きなプロジェクトに遭遇したことはありません。
うーん。 rerun
は何年もの間不可欠でした - 私は基本的にテストを実行したり、ドットファイルを編集しながらドットファイルを再構築したりするために毎日8時間使用しています。 inotifyを使用し(私はもうWindowsやOSXを使用していません。)、Bashで書かれているソリューションです(だから、余分な手間をかけずにエイリアスで動作します)。
説明
これはファイルの変更を監視し、どんなコマンドでも実行します(さらなる引数を含む) 2番目のステートメントとして与えられたそれはまたスクリーンをクリアしそして最後の実行の時間を印刷するでしょう。注:各whileループサイクルの後に関数がスリープする秒数を変更することで、関数をより反応性にすることができます。
使用例
watch_file my_file.php php my_file.php
この行はphpファイルのmy_file.php
を監視し、それが変わるたびにphp
インタプリタを実行します。
関数定義
function watch_file (){
### Set initial time of file
LTIME=`stat -c %Z $1`
printf "\033c"
echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
${@:2}
while true
do
ATIME=`stat -c %Z $1`
if [[ "$ATIME" != "$LTIME" ]]
then
printf "\033c"
echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
${@:2}
LTIME=$ATIME
fi
sleep 1
done
}
クレジット
これは基本的にVDRの答えのより一般的なバージョンです。
ちょっと違う状況がありました。しかし、私はこれがこの質問を読んでいる人には役に立つかもしれないと感じます。
ログファイルのサイズが変更されたときに通知を受ける必要がありましたが、すぐには必要ありませんでした。そしてそれは将来的には数日あるいは数週間になるかもしれないので、私はinotify
(とにかくそのサーバーにインストール/起動されていない)をコマンドラインで使うことができませんでした(Nohup
などは使いたくありません)。それで私はチェックするためにcron
でbashスクリプトを実行することにしました
スクリプトは監視されているファイルのファイルサイズをテキストファイルに書き込み、cron
を実行するたびにその値が変更されているかどうかをチェックし、変更されている場合は最後の行を私にメールします。
#!/bin/bash
FILE_TO_WATCH="/path/to/log_file.log"
FILESIZE_FILE="/path_to/record.txt"
SUBJECT="Log file 'log_file.log' has changed"
MAILTO="[email protected]"
BODY="Last line of log file:\n"
LAST_LINES=1
# get old recorded file size from file
OLD_FILESIZE=$(cat "${FILESIZE_FILE}")
# write current file size into file
stat --printf="%s" "${FILE_TO_WATCH}" > "${FILESIZE_FILE}"
# get new recorded file size from file
NEW_FILESIZE=$(cat "${FILESIZE_FILE}")
if [ "${OLD_FILESIZE}" != "${NEW_FILESIZE}" ]; then
echo -e "${BODY}"$(tail -${LAST_LINES} ${FILE_TO_WATCH}) | mail -s "${SUBJECT}" "${MAILTO}"
fi