Linuxで、あるファイルが変更されたら特定のコマンドを実行する場面というのは非常に多いです。例えば、プログラムのソースコードが変更されたら自動的にウェブサーバーをリロードする、Apacheウェブサーバーであれば、httpd.confファイルやssl.confファイルが変更されたら、即座に文法チェックコマンドを実行するような場合です。そのような場合には「entr」というパッケージを使うことによって、ファイルの変更があったら即座にコマンドを実行することが非常に簡単に出来ます。
Linuxにはファイルの変更を検知してくれる「inotifywait」というコマンドがありますが、これは変更が発生した時に通知はしてくれますが、そのままコマンドを実行する機能は備わっていません。別途シェルスクリプトを書いたりする必要があります。一方、entrコマンドではファイルの変更を検知したら簡単に任意のコマンドを実行することが可能です。
では早速entrパッケージをインストールして使ってみましょう。
entrコマンドのインストール
Ubuntuであれば、以下のようにentrパッケージをインストールします。
$ sudo apt-get install entr
公式のGitHubリポジトリはこちらになります。
entrコマンドの使い方
既に存在するファイルの変更を検知して任意のコマンドを実行する
次のようにtest.txtという空のテキストファイルを作成します。このファイルに変更があったら、指定したコマンドを実行する、という動きをentrを使って実行してみます。
$ touch test.txt
まず監視対象のファイルを指定する必要がありますので、ここでは「ls test.txt」というコマンドで単純にlsコマンドでtest.txtファイルを表示します。そしてパイプ(|)でentrコマンドにファイル名を渡してあげます。さらにファイルの変更が検知されたら「echo “Hello World.”」というコマンドを実行するようにします。
$ ls test.txt | entr echo "Hello World."
Hello World.
実行するとすぐに「Hello World.」と画面に表示されます。これは最初にentrコマンドが実行された時点で、コマンドが1度実行されるというデフォルトの動作です。
次に実際にtest.txtファイルに変更を加えてみましょう。entrコマンドが実行されているターミナルはそのままにしつつ、別ターミナルを開いて、ここではtest.txtファイルに「1」という数字をechoコマンドで書き込んでみます。viエディタなどで変更を加えてみても大丈夫です。
$ echo "1" > test.txt
するとentrコマンドが実行されているターミナルには再度「Hello World.」と表示されます。
$ ls test.txt | entr echo "Hello World."
Hello World.
Hello World.
このようにファイルが変更されたら即座に指定したコマンドを実行できます。
最初にコマンドを実行したくない場合には「-p」オプションを付けることで、最初にコマンドが実行されないようにすることが出来ます。デフォルトでは最初にコマンドが実行されますが、「-p」オプションを付けることで、ファイルの変更が行われるまでコマンドの実行を遅延する(Postpone)という動作にすることが出来ます。
$ ls test.txt | entr -p echo "Hello World."
以降は全て「-p」オプションを付けて実行していきます。
複数ファイルの変更を検知して任意のコマンドを実行する
先の例では1ファイルだけの変更を検知して任意のコマンドを実行できるようにしました。entrコマンドでは複数のファイルを監視して、変更があったら任意のコマンドを実行できるようにすることが出来ます。
複数のファイルを監視できるように、以下のようにtestディレクトリの中に、1.txt、2.txt、3.txt、aaaディレクトリ、bbbディレクトリ、それぞれのディレクトリ内にaaa.txt、bbb.txtという5つのファイルを用意します。
$ tree ./test
./test
├── 1.txt
├── 2.txt
├── 3.txt
├── aaa
│ └── aaa.txt
└── bbb
└── bbb.txt
3 directories, 5 files
このtestディレクトリ内の全てのテキストファイルを監視して、変更があれば任意のコマンドを実行できるように、以下のようにコマンドを実行します。findコマンドでtestディレクトリ内のファイルを全てリストアップして、ファイル名一覧をentrコマンドに渡してあげます。そうすると、それらのファイル全ての変更を監視するという動作になります。
$ find ./test | entr -p echo "変更を検知しました。"
次のようにしてサブディレクトリに存在するtest/aaa/aaa.txtファイルを変更してみます。
$ echo "Hello" > test/aaa/aaa.txt
するとファイルの変更が検知されて、コマンドが実行されて「変更を検知しました」というメッセージが表示されます。ディレクトリの奥深くのファイルでも変更があれば検知されることが分かります。
$ find ./test | entr -p echo "変更を検知しました。"
変更を検知しました。
ディレクトリを監視することによって、ディレクトリ内のファイルのうち1つでも変更されたらコマンドを実行する、ということが出来ます。
なお、既にentrコマンドを実行中の状態で、次のように新しいファイルを作成してもコマンドは実行されず何も起きません。つまりentrコマンドが実行された時点で存在しているファイルのみが監視対象ということになります。
$ touch test/hoge.txt
ディレクトリ内に新しいファイルが作成されたらentrコマンドが終了する動作に変更することもできます。その場合には「-d」オプションを付けてentrコマンドを実行します。「-d」オプションはディレクトリ内の変更を常に監視してくれるオプションです。以下のようにtestディレクトリの中に4.txtという新しいファイルを作成します。
$ touch test/4.txt
すると以下のように「変更を検知しました」と表示され、ディレクトリの内容が変更されたというメッセージ(entr: directory altered)が表示されて、entrコマンドが終了します。
$ find ./test | entr -p -d echo "変更を検知しました。"
変更を検知しました。
entr: directory altered
なお監視対象のファイルが削除されると、ファイルが見つからないというエラーが発生してentrコマンドの実行は終了してしまいます。
$ find ./test | entr -p echo "変更を検知しました。"
entr: cannot open './test/1.txt': No such file or directory
ファイルの変更が無くても、手動でコマンドを実行する
entrコマンド実行中は、対話型でコマンドを受けてくれます。以下のようにentrコマンドを実行してファイルの変更を監視中の状態で、キーボードでスペースキーを押すと、entrコマンドで指定されているコマンドを実行することが出来ます。ファイルの変更が無くてもキーボードでスペースキーを押下すればコマンドを実行できます。
$ find ./test | entr -p -d echo "変更を検知しました。"
(ここでスペースキーを押す)
変更を検知しました。
また、entrコマンド実行中にキーボードで「q」を押下することにより、entrコマンドを終了させることもできます。
キーボードでの操作を受け付けなくするようにするには、次のようにentrコマンドに「-n」オプションを付けると対話型モードが無効化されます。この状態でスペースキーや「q」を押してもコマンドは実行されず、entrコマンドはそのままファイルの変更を監視し続けてくれます。
$ find ./test | entr -p -d -n echo "変更を検知しました。"
ファイルの変更を検知した時に、複数コマンドを実行する
これまでの例では、「echo “変更を検知しました。”」というように、1つのコマンドだけを実行していました。変更が検知された時に複数のコマンドを「echo “変更を検知しました(1)。” && echo “変更を検知しました(2)。”」のように並べた場合、次のように最初のechoコマンドしか実行されません。
$ find ./test | entr -p echo "変更を検知しました(1)。" && echo "変更を検知しました(2)。"
変更を検知しました(1)。
ファイルの変更が検知された時に複数のコマンドを実行したい場合、「-s」オプションを付けて複数コマンドをシングルクォーテーション(‘)で括ってentrコマンドを実行しておきます。以下のように複数コマンドが実行されたことが分かります。
$ find ./test | entr -p -s 'echo "変更を検知しました(1)。" && echo "変更を検知しました(2)。"'
別のターミナルで1.txtファイルに変更を加えてみましょう。
$ touch 1.txt
すると次のように複数コマンドが実行されたことが分かります。最後に使っているシェル(この場合はzsh)の終了コードも表示されます。これによってコマンドが正常終了したかどうかが分かります。
$ find ./test | entr -p -s 'echo "変更を検知しました(1)。" && echo "変更を検知しました(2)。"'
変更を検知しました(1)。
変更を検知しました(2)。
zsh returned exit code 0
まとめ
entrコマンドを使うと、Linuxで特定のファイルの変更があったら任意のコマンドを実行するという動作をさせることが出来ます。設定ファイルなどを用意することなく、手軽にファイル監視を設定してコマンドを実行させることが出来るのは非常に便利です。アイデア次第で様々な活用方法が考えられますので、今後も活用方法を追記していこうと思います。