LoginSignup
1
0

More than 5 years have passed since last update.

guard-shellで複数ファイルが更新されたとき何回もコマンド実行されないようにする

Last updated at Posted at 2018-09-09

やりたいこと

  • Rubyのguard gemとguard-shellプラグインを使っている
  • プロジェクト内のファイルを監視し、更新されたときプロジェクト全体をビルドしたい
  • 通常、Guardでは1ファイルの更新につきそのファイルを処理するコマンドが1回走るので、touch hoge.c moge.c のように同時に複数のファイルが更新された場合、プロジェクト全体をビルドするコマンドが2回走ってしまう
  • これを1回だけにしたい

解決法

こんな感じでGuardfileを書いておく:

$last_exec_time = Time.now
guard :shell do
  watch(/.*\.(c|h)$/) do |m|
    puts "Changed: #{m[0]}"
    # 前回実行から1秒以内なら実行しない
    if Time.now - $last_exec_time < 1
      puts "Skip"
      next
    end
    ret = `sleep 2; date`  # 実験用のコマンド
    $last_exec_time = Time.now
    ret
  end
end

グローバル変数$last_exec_timeを使い、前回実行から1秒以内ならスキップするようにしている。
ビルドコマンドに1秒以上かかってもちゃんとスキップされている。

やりたいことは非常によくあることな割に、ダーティなやり方である。
もっといい方法があったら教えてください。

[追記]
guard-processguard-rakeを使えば今回のような問題は生じない。
guard-processならGuardfile内にコマンドを書けるので手軽にguard-shellの代用にできる。

そもそもguard-shellは作りがguardの思想から少し外れているような気がする。
watchのブロックは

When you add a block to the watch expression, you can modify the file name that has been detected before sending it to the plugin for processing:
(中略)
NOTE: Normally, most plugins expect the block to return a path or array of paths
https://github.com/guard/guard/wiki/Guardfile-DSL---Configuring-Guard#watch

と書いてある通り、変更されたファイル名から処理すべきファイル名に変換するのが本来の目的であり、その中でシェル呼び出しすればそりゃ複数回呼ばれるよな。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0